I am using the file_picker plugin to pick a CSV file in Flutter Web. Although I am able to pick the file it is converting the file into bytes (Uint8List). Is there any way I can get the original CSV file or if I can convert these bytes to CSV maybe I can get the path of the file?
Code:
void pickCSV() async {
FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['csv']);
if (result != null) {
var fileBytes = result.files.first.bytes;
csfFileName.value = result.files.first.name;
} else {
// User canceled the picker
}
}
I know it's a bit late but you have a couple of choices and maybe it helps others out aswell.
Both of the choices requires server-side processing, so you will need to read on how to do that.
Get the content of the CSV file send it to the server and make a new file on the server with that content. You can use String.fromCharCodes to read the content, in the web, after you select the file.
Convert the Uint8List into a base64 string, using base64Encode function, send it to the server, process it there.
Alternatively, if you use Firebase Storage you can use putData like so:
final metaData = SettableMetadata(contentType: mimeType);
final task = await _storage.ref().child(cloudPath).putData(fileData, metaData);
/// Get the URL
await task.ref.getDownloadURL()
Storing the mimeType ensures proper handling of the file when used after download
cloudPath means the location in FirebaseStorage, such as: storageDirectory/filename.extension
fileData is the Uint8List you provided
Related
SOLVED FOR MY SITUATION. MORE INFORMATION HERE AND THEN ORIGINAL QUESTION BELOW.
===Solution===
Due to settings with Android external storage, the file_picker plugin creates a cache of the file you pick and stores it in a cache directory within the app storage location. It will not overwrite this for files with the same name on subsequent reads. So for my read/write app, the solution was to do await file.delete(); when I was done with the read operation. This ensures that the next read will then create a cached version with the updated contents
===Original Question===
I have some content in a database on a Flutter app I am using to just practice some new stuff in FLutter. I have an export button that gets this data, JSON encodes it, and writes it to a file.
If I change the content and then export a second time, I can open the file on my device and see the updated content. I also have an import button. When I press that, I use FilePicker to select a file, read the contents of the file, and then JSON decode the data into an object.
I print out the file.readAsString and see the content from the initial write.
If I manually delete the file between writes then it works. If I use file.delete() before the write, it does not work. What can I do to get the updated text when I read from the file?
Getting file to write to. (I am aware this will only work on Android as is and that's fine)
Future<File?> _getBackupDataFile(String pathToTryFirst, ExportData data) async {
Directory? directory = Directory(pathToTryFirst);
if (!await directory.exists()) directory = await getExternalStorageDirectory();
if ((await directory?.exists() ?? false) == false) {
showErrorDialog(context: context, body: "Unable to find directory to save file.");
return null;
}
return File("${directory?.path}/pm-account-backup.json");
}
Write to file as such (without the delete code):
Future<void> _writeDataToFile(ExportData data) async {
try {
File? file = await _getBackupDataFile('/storage/emulated/0/Download', data);
if(file == null) { return; }
await file.writeAsString(jsonEncode(data));
await showSuccessDialog(context: context, title: "Success", body: "${data.accounts.length} accounts backed up successfully.");
} catch (e) {
showErrorDialog(context: context, body: "Failed to write data to file.");
}
}
Simplified file pick:
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
String path = result.files.single.path ?? '';
if((path).endsWith(".json")) {
return File(path);
}
}
Read from file as such:
String fileData = await file.readAsString();
print(fileData);
Solution for my question found after more information provided by #pskink
Due to settings with Android external storage, the file_picker plugin creates a cache of the file you pick and stores it in a cache directory within the app storage location. It will not overwrite this for files with the same name on subsequent reads. So for my read/write app, the solution was to do await file.delete(); when I was done with the read operation. This ensures that the next read will then create a cached version with the updated contents
Iam uploading files to firebase storage and the referenced url in cloudfirestore. Uploading and showing the file in the app works perfectly.
But when i try to delete an image, i get an error:
"[firebase_storage/object-not-found] No object exists at the desired reference."
I found out that the urls in firebasestorage and in cloudfirestore are not the same:
FirebaseStorage URL: https://firebasestorage.googleapis.com/v0/b/project-db68d.appspot.com/o/images%2FNvbKO7fZxv5KXsPy1lPJovsxiKXN%2Fimage_cropper_1662715164516_out.jpg?alt=media&token=2d591f0d-d2ee-4640-8133-57cea509d3d7 //Does not show the file in the browser
CloudFirestore URL: gs://project-db68d.appspot.com/images/NvbKO7fZxv5KXsPy1lPJovsxiKXN/image_cropper_1662715164516_out.jpg // Shows the file in der browser
I don`t understand why 2 different urls are created and how to fit it, when i print the url it shows the url from firestorage?
This is my code:
Iam working with ImagePicker, flutter_image_compress and image_cropper, latest versions flutter and packages
Future<File?> getImageFromCamera()async{
File receivedImageFromCamera = await _pickImageFromDevice.pickSingleImageFromGallerieOrCamera(ImageSource.camera);
File receivedCroppedImage = await _croppImageFromDevice.imageCropper(receivedImageFromCamera);
File? compressedFile = (await _imageCompressor.compressFile(receivedCroppedImage));
return compressedFile;
}
static Future<String> uploadFile(String destination,File file)async{
final ref = FirebaseStorage.instance.ref(destination);
final result = await ref.putFile(file);
final String fileUrl = (await result.ref.getDownloadURL()).toString();
return fileUrl;
}
if(compressedFile==null) return;
final fileName = basename(compressedFile.path);
final destination = 'images/$chatId/$fileName';
final fileUrl = await UploadFileToStorage.uploadFile(destination,compressedFile);
Both URLs are valid references to the file, but they have a different protocol. The gs:// protocol is specific to Google Cloud Storage, and is supported by very few clients. The https:// protocol is universal and supported almost everywhere.
I am currently developing a mobile app with flutter and I want to display the profile picture of a user and their friends.
The pictures are stored in Firebase Storage, but to minimize the number of requests I want to load each image once and then store it locally.
I wrote a function to get the image from Firebase storage and store it in the app document directory, but I can't figure out how to execute the function only when the file doesn't already exist locally.
This is an excerpt of my code
Future<File?> getProfilePic(String? uid) async {
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
String filepath = "${appDocumentsDirectory.path}/profiles/$uid.jpg";
if (await File(filepath).exists()) {
return File(filepath);
} else {
var success = await downloadFromFirebaseAndSaveToLocal(
"profilepic/$uid.jpg", filepath);
if (success) {
getProfilePic(uid);
}
}
return null;
}
Somehow the condition of the if statement (await File(filepath).exists()) is always true. I guess it makes sense. I think the file exists in the directory but doesn't have any content.
Does anyone know how to check if the file has content?
A normal null check doesn't work.
Thank you for helping!
To check the size of the file, you can
if (File(filepath).lengthSync() != 0)
or
You can await the file.length()
So I already read about the topic but I simply didn't understand the solutions on stack.
I came up with this code:
Im saving a url looking like this:
final String myDataUrl = file.url;
print(myDataUrl);
blob:http://localhost:51947/2952a3b1-db6a-4882-a42a-8e1bf0a0ad73
& then Im trying to add it into Firebase Storage with the putString operator, that I guessed that suited me best while reading the Documentation. I thought that I have a Url and therefore should be able to upload it like this:
FirebaseStorage.instance
.ref()
.child("bla")
.putString(myDataUrl, format: PutStringFormat.dataUrl);
But it doesn't work, it says that:
Error: Invalid argument (uri): Scheme must be 'data': Instance of '_Uri'
So Im guessing that it somehow can't format my url to one that is accepted.
What can I do different to upload a blob successfully to firebase Storage?
-----------------Answer----------------------
Answer in the comment of the answer.
You have to convert your Blob to a Uint8List & upload it like:
Future<Uint8List> fileConverter() async {
final reader = html.FileReader();
reader.readAsArrayBuffer(file!);
await reader.onLoad.first;
return reader.result as Uint8List;
}
and then put it into your Storage:
Future uploadFile(String uid) async {
if (file == null) return;
final path = "nachweise/$uid";
Uint8List fileConverted = await fileConverter();
try {
FirebaseStorage.instance
.ref()
.child(path)
.putData(fileConverted)
.then((bla) => print("sucess"));
} on FirebaseException catch (e) {
return null;
}
}
The Firebase Storage SDKs can upload local data as either a File, an array of bytes, or a base-64 encoded string. The only URLs it accepts are so-called data URLs, which start with data:// and contain the complete data of the object. They cannot upload data directly from URLs that you more commonly see, such as http:// or https://.
You'll need to first download the data from that URL to the local device, and then upload it from there.
I am still struggiling with this annoying error. I have base64 string which I want to convert to Image. Here is the simpliest piece of code which is doing exactly what I want (at least, I saw it in different answers and code samples on the SO). I am getting the error:
Invalid character (at character 6)
my code is:
final String encodedStr = 'https://securelink.com/cameratypes/picture/13/true';
Uint8List bytes = base64.decode(encodedStr);
and i want to disply image:
Image.memory(bytes)
Finally, I found the solution, I don't know if it is important and will be useful to anyone who is struggling like me, but I am going to help. So, it would be easy and quick because I have already converted my image to nedeed formart (my image is base64 format), i made a dumb mistake when I was trying to convert it in String again, because it is already a String and I need Uint8List format. Side note: if your api devs said it should take a cookie or any kind of auth, it should be so.
code:
Future<String> _createFileFromString() async {
final response = await http.get(
Uri.parse(
'your link here',
),
headers: {
'cookie':
'your cookie here'
});
final Uint8List bytes = response.bodyBytes;
String dir = (await getApplicationDocumentsDirectory()).path;
String fullPath = '$dir/abc.png';
print("local file full path ${fullPath}");
File file = File(fullPath);
await file.writeAsBytes(List.from(bytes));
print(file.path);
final result = await ImageGallerySaver.saveImage(bytes);
print(result);
return file.path;
}
This code saves your image in straight to the app gallery and do not display on the screen anything
If your URI that contains data after comma as it is defined by RFC-2397. Dart's Uri class is based on RFC-3986, so you can't use it. Split the string by comma and take the last part of it:
String uri = 'data:image/gif;base64,...';
Uint8List _bytes = base64.decode(uri.split(',').last);
REFERENCE: https://stackoverflow.com/a/59015116/12382178