Search code examples
flutterdartgoogle-apigoogle-drive-apidio

Google Drive API: Uploading and creating folders with http requests, for example in DIO with Flutter


I'm trying to create a simple Flutter app which interacts with the Google Drive API. Authentication works great via the Google Sign In package, so I have access to the correct headers and auth tokens.

What I don't understand however, despite trying different approaches and reading the Drive Documentation up and down - how can I interact with the API via http requests, for example via Dio or via the "standard" way in dart/flutter?

To state one example: I want to upload an an image the user picked. I have everything figured out (the file path, the auth token, etc.), but how does a http request look like?

Here is the "bare" http request:

Map headers = await user.currentUser.authHeaders;

var formData = FormData.fromMap({
    'name': filePath,
    'file': MultipartFile.fromBytes(fileData, filename: filePath)
  });

var response = await Dio().post(
      'https://www.googleapis.com/upload/drive/v3/files?uploadType=media',
       data: formData,
      options: Options(headers: headers));

  print(response);

It's probably a very mundane/trivial question, but I just can't figure it out ..

Thanks in advance for your help! Christian


Solution

  • You need to create the File first then upload the file data into it.

    I'll using the http plugin and not DIO. But the same process should work for dio.

    Step one: Create the file metadata in a folder

    Future<String> createFile({File image, String folderId}) async {
      String accessToken = await Prefs.getToken();
      Map body = {
        'name': 'name.jpg',
        'description': 'Newly created file',
        'mimeType': 'application/octet-stream',
        'parents': ['$folderId']
      };
      var res = await http.post(
        'https://www.googleapis.com/drive/v3/files',
        headers: {
          'Authorization': 'Bearer $accessToken',
          'Content-Type': 'application/json; charset=UTF-8'
        },
        body: jsonEncode(body),
      );
      if (res.statusCode == 200) {
        // Extract the ID of the file we just created so we 
        // can upload file data into it
        String fileId = jsonDecode(res.body)['id'];
        // Upload the content into the empty file
        await uploadImageToFile(image, fileId);
        // Get file (downloadable) link and use it for anything
        String link = await getFileLink(fileId);
        return link;
      } else {
        Map json = jsonDecode(res.body);
        throw ('${json['error']['message']}');
      }
    }
    

    Step two: Upload image data into empty file

    Future uploadImageToFile(File image, String id) async {
      String accessToken = await Prefs.getToken();
      String mimeType = mime(basename(image.path).toLowerCase());
      print(mimeType);
      var res = await http.patch(
        'https://www.googleapis.com/upload/drive/v3/files/$id?uploadType=media',
        body: image.readAsBytesSync(),
        headers: {
          'Authorization': 'Bearer $accessToken',
          'Content-Type': '$mimeType'
        },
      );
      if (res.statusCode == 200) {
        return res.body;
      } else {
        Map json = jsonDecode(res.body);
        throw ('${json['error']['message']}');
      }
    }
    

    Step three: Get downloadable file link(to store in database or use for anything)

    Future getFileLink(String id) async {
      String accessToken = await Prefs.getToken();
      var res = await http.get(
        'https://www.googleapis.com/drive/v3/files/$id?fields=webContentLink',
        headers: {
          'Authorization': 'Bearer $accessToken',
          'Content-Type': 'application/json; charset=UTF-8'
        },
      );
      if (res.statusCode == 200) {
        Map json = jsonDecode(res.body);
        String link = json['webContentLink'];
        return link.split('&')[0];
      } else {
        Map json = jsonDecode(res.body);
        throw ('${json['error']['message']}');
      }
    }