How can I download multiple images from Firebase Storage on Flutter Web? - flutter

I am developing a Flutter Web application that, after clicking a download button, I need to download multiple images from Firebase Storage.
How can I don this on Flutter Web?
UPDATE:
After following Frank's suggestion on the comments, and other posts. I wrote the following function:
Future<void> downloadProductImages() async {
//TODO: modify on suppliers admin that images are saved with the correct name.
final storageRef = FirebaseStorage.instance.ref();
int count = 1;
for (final ref in product.imgsMap.keys.toList()) {
print(ref);
try {
final childRef = storageRef.child(ref);
const maxSize = 10 * 1024 * 1024;
final Uint8List data = (await childRef.getData(maxSize))!;
//check if file is jpeg
late String ext;
if (data[0] == 0xFF &&
data[1] == 0xD8 &&
data[data.length - 2] == 0xFF &&
data[data.length - 1] == 0xD9) {
ext = 'jpeg';
} else {
ext = 'png';
}
// Data for "images/island.jpg" is returned, use this as needed.
final content = base64Encode(data!.toList());
AnchorElement(
href:
"data:application/octet-stream;charset=utf-16le;base64,$content")
//href: "image/$ext;charset=utf-16le;base64,$content")
..setAttribute("download", "${product.name}_$count.$ext")
..click();
count++;
} on FirebaseException catch (e) {
//TODO: HANDLE EXCEPTIONS
print(e);
} on Exception catch (e) {
// Handle any errors.
print(e);
}
}
// File file = // generated somewhere
// final rawData = file.readAsBytesSync();
}
On chrome mobile, the files are downloaded but are not recognized as pictures. It seems that the AnchorElment doesn't have the correct href.
Any ideas?

Related

How can I download multiple images from Firestore Storage on a Flutter Web mobile app?

Following Firestore documentation and other posts, I arrived at the following function:
Future<void> downloadProductImages() async {
//TODO: modify on suppliers admin that images are saved with the correct name.
final storageRef = FirebaseStorage.instance.ref();
int count = 1;
for (final ref in product.imgsMap.keys.toList()) {
print(ref);
try {
final childRef = storageRef.child(ref);
const maxSize = 10 * 1024 * 1024;
final Uint8List data = (await childRef.getData(maxSize))!;
//check if file is jpeg
late String ext;
if (data[0] == 0xFF &&
data[1] == 0xD8 &&
data[data.length - 2] == 0xFF &&
data[data.length - 1] == 0xD9) {
ext = 'jpeg';
} else {
ext = 'png';
}
final content = base64Encode(data!.toList());
AnchorElement(href: "${product.imgsMap[ref]}")
..setAttribute("download", " ${product.name}_$count.$ext")
..setAttribute("type", "image/$ext")
..click();
count++;
} on FirebaseException catch (e) {
//TODO: HANDLE EXCEPTIONS
print(e);
} on Exception catch (e) {
// Handle any errors.
print(e);
}
}
// File file = // generated somewhere
// final rawData = file.readAsBytesSync();
}
I am able to download multiples files, however the images are not recognized as such on Chrome Mobile's downloads, which is my main target.
I am guessing that I am not building correctly the Anchor Element.
How can I fix this method or is there a different way to download multiple images on Flutter Web?

Flutter app keeps stoping when i'm trying to extarct text from image using google_ml_vision: ^0.0.7

I'm trying to extract Arabic text from an image using google_ml_vision: ^0.0.7,
I wrote the function below, but when I call it the app crashed(its crashed in this point await textRecognizer.processImage(visionImage)) without providing any logs
the image path is correct but it still didn't work
Future _extarctArabicTexFromImage() async {
try {
// final File imageFile = File(imgPath);
final GoogleVisionImage visionImage =
GoogleVisionImage.fromFilePath(imgPath);
final TextRecognizer textRecognizer =
GoogleVision.instance.textRecognizer();
late VisionText visionText;
try {
visionText = await textRecognizer.processImage(visionImage);
} catch (e) {
print(e.toString());
}
// String text = visionText.text;
for (var block in visionText.blocks) {
final List<RecognizedLanguage> languages = block.recognizedLanguages;
for (var line in block.lines) {
for (var textElement in line.elements) {
txt = txt + " " + textElement.text.toString();
}
txt = txt + '\n';
}
}
} catch (e) {
print(e.toString());
}
}

How to deploy initial Data to Flutter App

I am stuck with the following problem:
We are writing a library app which contains a bunch of internal documents (roughly 900).
The total size of all docs is around 2,5+ GB.
I have to find a way how to initialise the app with 2,5GB of Docs on first start.
My idea was to create a zip file of the initial load, download it from server and unpack it during setup on first start. However I am not finding a way how to unzip such a big file, as all solutions read the zip to memory first (completely) before writing it to storage.
I also do not want to make 900+ calls to our web-server to download the Documents on first start.
Deployment target is iOS and Android, possibly win+mac later on.
Any ideas?
i tested it on flutter linux where Uri.base points to the root of the project (the folder with pubspec.yaml, README.md etc) - if you run it on android / iOS check where Uri.base points to and change baseUri if it is not good location:
final debug = true;
final Set<Uri> foldersCreated = {};
final baseUri = Uri.base;
final baseUriLength = baseUri.toString().length;
final zipFileUri = baseUri.resolve('inputFolder/backup.zip');
final outputFolderUri = baseUri.resolve('outputFolder/');
print('0. files will be stored in [$outputFolderUri]');
final list = FileList(zipFileUri, debug: debug);
print('1. reading ZipDirectory...');
final directory = ZipDirectory.read(InputStream(list));
print('2. iterating over ZipDirectory file headers...');
for (final zfh in directory.fileHeaders) {
final zf = zfh.file;
final content = zf.content;
// writing file
final uri = outputFolderUri.resolve(zf.filename);
final folderUri = uri.resolve('.');
if (foldersCreated.add(folderUri)) {
if (debug) print(' #### creating folder [${folderUri.toString().substring(baseUriLength)}] #### ');
Directory.fromUri(folderUri).createSync(recursive: true);
}
File.fromUri(uri).writeAsBytesSync(content);
print("file: [${zf.filename}], compressed: ${zf.compressedSize}, uncompressed: ${zf.uncompressedSize}, length: ${content.length}");
}
list.close();
print('3. all done!');
and here is a List backed by a LruMap that reads data in chunks from your huge zip file:
class FileList with ListMixin<int> {
RandomAccessFile _file;
LruMap<int, List<int>> _cache;
final int maximumPages;
final int pageSize;
final bool debug;
FileList(Uri uri, {
this.pageSize = 1024, // 1024 is just for tests: make it bigger (1024 * 1024 for example) for normal use
this.maximumPages = 4, // maybe even 2 is good enough?
this.debug = false,
}) {
_file = File.fromUri(uri).openSync();
length = _file.lengthSync();
_cache = LruMap(maximumSize: maximumPages);
}
void close() => _file.closeSync();
#override
int length;
int minIndex = -1;
int maxIndex = -1;
List<int> page;
#override
int operator [](int index) {
// print(index);
// 1st cache level
if (index >= minIndex && index < maxIndex) {
return page[index - minIndex];
}
// 2nd cache level
int key = index ~/ pageSize;
final pagePosition = key * pageSize;
page = _cache.putIfAbsent(key, () {
if (debug) print(' #### reading page #$key (position $pagePosition) #### ');
_file.setPositionSync(pagePosition);
return _file.readSync(pageSize);
});
minIndex = pagePosition;
maxIndex = pagePosition + pageSize;
return page[index - pagePosition];
}
#override
void operator []=(int index, int value) => null;
}
you can play with pageSize and maximumPages to find the optimal solution - i think you can start with pageSize: 1024 * 1024 and maximumPages: 4 but you have to check it by yourself
of course all of that code should be run in some Isolate since it takes a lot of time to unzip couple of GB and then your UI will freeze, but first run it as it is and see the logs
EDIT
it seems that ZipFile.content has some memory leaks so the alternative could be a "tar file" based solution, it uses tar package and since it reads a Stream as an input you can use compressed *.tar.gz files (your Documents.tar had 17408 bytes while Documents.tar.gz has 993 bytes), notice that you can even read your data directly from the socket's stream so no need for any intermediate .tar.gz file:
final baseUri = Uri.base;
final tarFileUri = baseUri.resolve('inputFolder/Documents.tar.gz');
final outputFolderUri = baseUri.resolve('outputFolder/');
print('0. files will be stored in [$outputFolderUri]');
final stream = File.fromUri(tarFileUri)
.openRead()
.transform(gzip.decoder);
final reader = TarReader(stream);
print('1. iterating over tar stream...');
while (await reader.moveNext()) {
final entry = reader.current;
if (entry.type == TypeFlag.dir) {
print("dir: [${entry.name}]");
final folderUri = outputFolderUri.resolve(entry.name);
await Directory.fromUri(folderUri).create(recursive: true);
}
if (entry.type == TypeFlag.reg) {
print("file: [${entry.name}], size: ${entry.size}");
final uri = outputFolderUri.resolve(entry.name);
await entry.contents.pipe(File.fromUri(uri).openWrite());
}
}
print('2. all done!');

Flutter: Convert the cached (.file) file to mp4 file

I have a video file. I save to cache this video file. And I want to play this video in other page. But when I save this video to cache I dont play video. this video appear in cache like this.
/data/user/0/com.example.flutter_cache_manager/cache/libCachedImageData/fc65e4a0-0fb7-11eb-a1ed-53020f3249ad.file
I need to convert this video to .mp4 file.
Future addCacheFiles() async {
try {
Uint8List bytes;
String myPath = realPath + '/WhatCarCanYouGetForAGrand.mp4';
_readFileByte(myPath).then((bytesData) async {
bytes = bytesData;
var file = await DefaultCacheManager().putFile(realPath + "/WhatCarCanYouGetForAGrand.mp4", bytes);
print(file.path.toString() + " " + "kayıt etti");
});
} catch (e) {
print("hata");
print(e);
}
}
Future<Uint8List> _readFileByte(String filePath) async {
Uri myUri = Uri.parse(filePath);
File audioFile = new File.fromUri(myUri);
Uint8List bytes;
await audioFile.readAsBytes().then((value) {
bytes = Uint8List.fromList(value);
print('reading of bytes is completed');
}).catchError((onError) {
print('Exception Error while reading audio from path:' +
onError.toString());
});
return bytes;
}
thanks. by the way this code is running. I just need to convert to mp4 file.
Do you definitely want to convert the cached file?
Instead you could include the fileExtension argument when you call .putFile:
var file = await DefaultCacheManager().putFile(realPath + "/WhatCarCanYouGetForAGrand.mp4", bytes, fileExtension: 'mp4');
That way the cache won't change it to .file in the first place.

How do I upload files from a phone to my Amazon S3 server?

I'm developing a mobile app with Unity and using S3 to store and retrieve assets, I can download asset bundles just fine from the server to the phone, but how do I upload files from the phone to the server?
I used the PostObject function from the AWS Unity SDK, and it works fine if I upload from the computer as I know the directory, but I'm not sure how to get the phone's photo gallery to upload to the s3 server.
This is the PostObject function
public void PostObject(string fileName)
{
ResultText.text = "Retrieving the file";
var stream = new FileStream("file://" + Application.streamingAssetsPath + "/" + fileName,
FileMode.Open, FileAccess.Read, FileShare.Read);
Debug.Log("kek");
ResultText.text += "\nCreating request object";
var request = new PostObjectRequest()
{
Bucket = S3BucketName,
Key = fileName,
InputStream = stream,
CannedACL = S3CannedACL.Private,
Region = _S3Region
};
ResultText.text += "\nMaking HTTP post call";
Client.PostObjectAsync(request, (responseObj) =>
{
if (responseObj.Exception == null)
{
ResultText.text += string.Format("\nobject {0} posted to bucket {1}",
responseObj.Request.Key, responseObj.Request.Bucket);
}
else
{
ResultText.text += "\nException while posting the result object";
ResultText.text += string.Format("\n receieved error {0}",
responseObj.Response.HttpStatusCode.ToString());
}
});
}
And this is where I'm using it to upload the picture taken from the phone to the server
public void TakePicture(int maxSize)
{
NativeCamera.Permission permission = NativeCamera.TakePicture((path) =>
{
Debug.Log("Image path: " + path);
if (path != null)
{
// Create a Texture2D from the captured image
Texture2D imageTexture = NativeCamera.LoadImageAtPath(path, maxSize);
if (imageTexture == null)
{
Debug.Log("Couldn't load texture from " + path);
return;
}
//picturePreview.gameObject.SetActive(true);
//picturePreview.texture = imageTexture;
Texture2D readableTexture = DuplicateTexture(imageTexture);
StartCoroutine(AddImageJob(readableTexture));
//Saves taken photo to the Image Gallery
if (isSaveFiles)
{
NativeGallery.SaveImageToGallery(imageTexture, "AReview", "test");
//Upload to Amazon S3
aws.PostObject(imageTexture.name);
aws.PostObject("test");
}
}
}, maxSize);
Debug.Log("Permission result: " + permission);
}
Any clues?
Thank you.