I'm developing a flutter app where I upload files to google drive which works like magic. I get the ID using response.id derived from DriveApi.files.create which looks something like this :
var createResponse = await drive.files.create(
fileToUpload,
uploadMedia: googledrive.Media(file.openRead(), file.lengthSync()),
)
Now, Let's say I want to update this file. In that case, I'm using the drive's update API. My call looks something like this :
var updateResponse = await drive.files.update(fileToUpload, fileId,
uploadMedia: googledrive.Media(file.openRead(), file.lengthSync()));
This fileId is the ID I get from create response (File's ID that gets returned after the create file)
The issue, however is, that I do NOT get any response in the updateResponse. I checked drive and nothing is getting updated. Any idea what I'm doing wrong? Any input would be really appreciated.
Related
In my app, I already integrated with google login and I successfully accessed my google drive folder and video files(mp4). But better player can play public video by modified url like this https://drive.google.com/uc?id=my_video_file_id.
I want to develop an app like Nplayer or Kudoplayer.
Can anyone guide me some scenario of Nplayer or Kudoplayer? and guide me how to play private drive's video using better player. Thanks.
Finally I got solution after research for a long time :-)
Summary answer for this question
Don't forget to add headers in BetterPlayerDataSource
Don't use this format "https://drive.google.com/uc?export=view&id=${fileId}" and
Use this format "https://www.googleapis.com/drive/v3/files/${fileId}?alt=media"
For guys who face issue like me, please follow these step
Enable Drive API
Integrate google login in your app with appropriate Scpoe, for me driveReadonlyScope is enough.
Retrieve fileId
Next step that I stuck is
Don't forget to add headers in BetterPlayerDataSource
Don't use this format "https://drive.google.com/uc?export=view&id=${fileId}" and
Use this format "https://www.googleapis.com/drive/v3/files/${fileId}?alt=media"
Like this
BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
"https://www.googleapis.com/drive/v3/files/$fileId?alt=media",
videoExtension: ".mp4",
headers: getDataFromCache(CacheManagerKey.authHeader),
);
authHeader can get when you call signWithGoogle function,
I save it and retrieve later. In signWithGoogle function, you can get authHeader using this
Map<String, String> authHeader = await googleSignIn.currentUser!.authHeaders;
Bonus - Queries For You.
Get Share With Me Data = > q: "'sharedWithMe = true and (mimeType = 'video/mp4' or mimeType = 'application/vnd.google-apps.folder')"
Get Share Drive Data => q: "'shared_drive_id' in parents and trashed=false and (mimeType = 'video/mp4' or mimeType = 'application/vnd.google-apps.folder')",
Get My Drive Data => q: "'root' in parents and trashed=false and (mimeType = 'video/mp4' or mimeType = 'application/vnd.google-apps.folder')",
that's tricky.
I would propably try to attach approprieate headers so google drive would allow me to reach file using BetterPlayerDataSource class.
but being curious and checking how google would want it to be made... I got into spiral of google docs and I found this
https://cloud.google.com/blog/products/identity-security/enhancing-security-controls-for-google-drive-third-party-apps
Keep in mind that downloading file and streaming also differ. So if you want for your player to not lag you should stream.
I want to get data from Firestore, convert it into csv format, then share it using the user's method of choice (e.g. email). I'm using the csv package to convert the data to csv, the path_provider to get the directory to write the file to the phone, and the share_plus package to share the file.
However, when I tap a share method (e.g. Gmail, Outlook, Whatsapp), it opens the right app but then I get a message on the phone like "Unable to attach file" (but there is no error in my app). The file is definitely being written as I can read it and it comes back with the expected string. The ShareResultStatus that is returned by the share is 'successful' Can anyone figure it out what the problem is and how to fix it?
Here is my code:
Future exportData() async {
// Dummy data (in reality, this will come from Firestore)
List<List> dummyData = [
['Date', 'Piece', 'Rating'],
[DateTime.utc(2022, 05, 01), 'Sonata', 4],
[DateTime.utc(2022, 05, 02), 'Sonata', 2],
];
// Turn into CSV
String csvData = _convertToCsv(dummyData);
// Write to local disk
await _writeData(csvData);
// Share
String filePath = await _filePath;
final result =
await Share.shareFilesWithResult([filePath], mimeTypes: ['text/csv']);
print(result.status);
}
And here are the functions used in above code:
Future<String> get _filePath async {
final directory = await getApplicationDocumentsDirectory();
return '${directory.path}/my_practice_diary_data.csv';
}
Future<File> _writeData(String csvData) async {
String filePath = await _filePath;
final file = File(filePath);
return file.writeAsString(csvData);
}
String _convertToCsv(List<List> data) {
String result = const ListToCsvConverter().convert(data);
return result;
}
Note: I've tried doing it both as txt and csv files, got same result
*** EDIT (06/06/2022): After a lot of reading and watching youtube videos, I have come to realise that the problem is that the directory getApplicationDocumentsDirectory() is only accessible by my app (and hence the app that is is being shared to, like gmail, cannot read it).
For now I have worked around it by using the package mailer and using user's google credentials to send emails (I followed these helpful youtube tutorials: Flutter Tutorial - How To Send Email In Background [2021] Without Backend and Flutter Tutorial - Google SignIn [2021] With Firebase Auth - Android, iOS, Flutter Web.
However, it would still be nice to know a nice way to generate a file and share it using share_plus, as per my original question. I believe that this can be achieved via one of two ways:
Find a way to allow other apps to access this specific file in the app directory; or
Find a way to download the file into an external storage (like downloads folder), then share that. (I cannot find a way to do this on both Android and iOS).
Anyone who knows how to do these or any other solution to the problem, please share!
I don't want to upload my code but I am having problems with retrieving multiple files from firebase storage. So in my app, I want to make users upload different files and while uploading it will show up in a list form.
The thing is I am able to save files into the firebase storage but I am freaking out trying to retrieve them into my application. I really hope if I get some to help me with this.
I don't exactly know what you're trying to achieve but maybe this would help.
When you upload your file to firestore storage, get a download link of that file and store it in the collection. Then you can retrieve the list of files you have uploaded.
Get the download URL of the uploaded file
UploadTask imageUploadTask = storageReference.putFile(file);
await imageUploadTask.whenComplete(() async {
mediaURL = await imageUploadTask.snapshot.ref.getDownloadURL();
print("download url = $mediaURL");
return mediaURL;
}).catchError((err){
print(err.toString());
});
Save URL to the collection
final CollectionReference _filesCollectionReference = FirebaseFirestore.instance.collection("files");
Map<String, String> file = {
"downloadURL" : url,
};
var result = await _filesCollectionReference.add(file);
Retrieve files
await _filesCollectionReference.get();
Background
In Google Apps Script I can access an existing document in Google Drive by calling DocumentApp and using the openById method. For example:
var documentToAccess = DocumentApp.openById(documentId);
Similarly, I can access a file in Google Drive bu calling DriveApp.getFileById. For example:
var fileToAccess = DriveApp.getFileById(documentId);
Problem
In Flutter, the googleapis dependency allows me to import several libraries including googleapis/drive/v3.dart, googleapis/docs/v1.dart, and googleapis/file/v1.dart which provide methods to access the attributes of files in Google Drive, but I can’t seem to find a method to actually get or open the file that I want to work on.
Question
In Flutter, how can I get a Google Docs document by its document ID so that I can then work on that document?
googleapis Documentation and Other Attempts
The googleapis documentation has some useful resources but despite attempting to implement these I can’t seem to get what I need. For example:
var fileToAccess = DriveApi().files.get('documentId’);
var fileToAccess = Document().documentId;
var fileToAccess = FilesResourceApi().get('documentId');
It looks like the user is not signed in, so first, sign in the user.
GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: <String>[
'email',
'https://www.googleapis.com/auth/documents.readonly',
'https://www.googleapis.com/auth/drive.readonly'
],
);
To instantiate the client you would add something like:
final client = MyClient(defaultHeaders: {
'Authorization': 'Bearer ${authentication.accessToken}'
});
and then where you try to get the document :
DriveApi driveApi = DriveApi(client);
var files = await driveApi.files
.list(q: 'mimeType=\'application/vnd.google-apps.document\'');
I want my users to be able to back-up their data on google drive. I used the "Try it now" tool of google drive to generate a file id, because if I don't set file id before uploading the file, the drive.files.create() method creates a new file id after every upload to google drive.
First I import google drive API.
import 'package:googleapis/drive/v3.dart' as ga;
This is how I create the file I want to upload:
// Create the file we want to upload.
ga.File fileToUpload = ga.File();
var file = await _localFile;
fileToUpload.parents = ["appDataFolder"];
fileToUpload.name = path.basename(file.absolute.path);
// Setting a generated id from the "Try it now" tool.
fileToUpload.id = "1azltlNpZEt6RyUBNwdYUAWPZv2o6w7bP";
And this is how I upload it to google drive:
var response = await drive.files.create(
fileToUpload,
uploadMedia: ga.Media(file.openRead(), file.lengthSync()),
);
The response I get is: "Unhandled Exception: DetailedApiRequestError(status: 403, message: Method not supported for files within the Application Data folder.)"
If I remove the next line when I create the file:
fileToUpload.parents = ["appDataFolder"];
And then I try to upload the file, I get: "DetailedApiRequestError(status: 403, message: The user does not have sufficient permissions for this file.)"
If I don't set the file id, which means I remove the next line:
fileToUpload.id = "1azltlNpZEt6RyUBNwdYUAWPZv2o6w7bP";
Then I can upload the file to google drive. But it's not helping me because it creates a new file id.
Is there anything I can do?
Edit: The problem with google drive generating a new file Id every time the user syncing the app data on google drive, is that it means that after every sync, I need to save the new file id on a server (so I can retrieve the file when needed). And when there are many active users that backing their data multiple times a day, all those update calls to the service would cost me too much money. So I prefer that the file Id will always stay the same.