Getting DetailedApiRequestError when uploading a file to google drive with pre-generated file id - flutter

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.

Related

Google Drive in Flutter | Update API not working

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.

Sharing a text file (csv) with share_plus: "unable to attach file"

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!

How to download a pdf file using Flutter?

void getHttp() async
{
print("Got called");
var response = await Dio().download('https://www.google.com/', 'assets/xx.html');
print("DDDDD:");
print(response);
}
The directory assets is present with read and write permissions.
I am calling this on the press of a button. "Got called" DOES get printed.
There are no errors present, still "DDDDD" doesn't get printed.
The xx.html doesn't get saved.
Where am I going wrong?
Based on documentation the syntax for Dio to download a file is
var response = await Dio().download('https://www.google.com/', <<Destination directory from which your app is running. Like internal storage or external storage.>>);
I guess you are providing the file path which is associated with the projects assets directory where the file cannot be downloaded since it is bundled with the app.
Use the following package to access the device file system and provide that path to download your file.
var response = await Dio().download('https://www.google.com/', <<Path from internal storage.>>);

How can I update google drive file in flutter?

I want to update drive files to shared = true.
but I get this error. How can I fix it?
E/flutter (14758): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: DetailedApiRequestError (status: 403, message: The resource body includes fields which are not directly writable.)
Future update() async {
var client = await getHttpClient();
var drive = ga.DriveApi(client);
print("Uploading file");
var metadata = new ga.File();
metadata..shared = true;
metadata..name = 'sst';
var response =
await drive.files.update(metadata, '1633r8UpI6gmJKtosLju3ThCQ2YR5AzKa');
print("Result ${response.toJson()}");
}
Issue:
As the error you're getting says, you are trying to update properties which are not directly writable.
Explanation:
Not all properties in the Files resource can be updated via Files: update. You can check which ones can and which ones cannot by looking at the properties list in the File resource page: the ones that can be directly updated are marked as writable on the Notes field. If you scroll down to the property shared, you will notice that it's not writable, and that's the reason you're getting this error.
The reason shared is not writable is that file sharing is not managed by the File resource, but by the Permissions resource. Files can be shared via Permissions: create (in which you have to specify who you're sharing the file with, and which role will be granted by this permission (view access, edit access, etc.).
Once the file has been shared (via Permissions: create, or directly through Drive UI), the property shared will automatically become true.
Reference:
Drive API: Share files, folders and drives

Is it possible to stream data(Upload) to store on bucket of Google cloud storage and allow to download at the same time?

Is it possible to stream data(Upload) to store on bucket of Google cloud storage and allow to download at the same time?
I have tried to use the Cloud API to upload a 100MB file to the bucket by using the code as below, but during the upload, and i refresh the bucket in the Google cloud console, i cannot see the new uploading file until the upload is finished. I would like to upload realtime video encoded in H.264 to store on the Cloud storage, so the size is unknown and at the same time, other users can start downloading the file event it is uploading. So is it possible?
Test code:
File tempFile = new File("StorageSample");
RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
try
{
raf.setLength(1000 * 1000 * 100);
}
finally
{
raf.close();
}
uploadFile(TEST_FILENAME, "text/plain", tempFile, bucketName);
public static void uploadFile(
String name, String contentType, File file, String bucketName)
throws IOException, GeneralSecurityException
{
InputStreamContent contentStream = new InputStreamContent(
contentType, new FileInputStream(file));
// Setting the length improves upload performance
contentStream.setLength(file.length());
StorageObject objectMetadata = new StorageObject()
// Set the destination object name
.setName(name)
// Set the access control list to publicly read-only
.setAcl(Arrays.asList(
new ObjectAccessControl().setEntity("allAuthenticatedUsers").setRole("READER"))); //allUsers//
// Do the insert
Storage client = StorageFactory.getService();
Storage.Objects.Insert insertRequest = client.objects().insert(
bucketName, objectMetadata, contentStream);
insertRequest.getMediaHttpUploader().setDirectUploadEnabled(false);
insertRequest.execute();
}
Unfortunately it's not possible, as state in the documentation:
Objects are immutable, which means that an uploaded object cannot
change throughout its storage lifetime. An object's storage lifetime
is the time between successful object creation (upload) and successful
object deletion.
This means that an object in cloud storage starts to exist when the upload it's finished, so you cannot access the object until your upload it's not completed.