I am implementing a chat application where users can share images. Each image is a stateful widget and each of them should get uploaded to the Firebase Storage as well.
My problem is, this flow works fine at the beginning of the app but when you upload another image, instead of the a single file, now 2 files are getting uploaded (1 new file and the file from the previous message).
I am pretty sure that this is something to do with keys so I provide an instance of UniqueKey as well but the problem is still there.
Let me explain my implementation and then provide the code:
I have 2 files; one is the chat screen, and the other one is a single message chip.
Chat screen keeps a list of message chips and does the rendering accordingly.
Message chip is stateful because, I want the user to see a progress while it is being uploaded to the server. After picking up a file from the device, an instance of message chip will get pushed to the array in the chat screen.
My code for the attach pic button in chat screen:
IconButton(
icon: Icon(Icons.attach_file),
onPressed: () async {
final File _file = await ImagePicker.pickImage(
source: ImageSource.gallery);
if (_file != null) {
//appending to the messages list
final sss = new MediaMessage(
key: UniqueKey(),
file: _file,
isImage: true,
threadId: widget._threadId,
);
setState(() {
_messages.add(sss);
});
}
},
)
and here is the code in my message chip file (including only the essentials)
void initState() {
super.initState();
//creating a file name eg: img_456985.jpg
final rand = Math.Random().nextInt(10000);
final fileExt = widget.file.path
.substring(widget.file.path.lastIndexOf('.'), widget.file.path.length);
_fileName = 'image_$rand$fileExt';
final StorageReference storeRef = FirebaseStorage.instance
.ref()
.child('threads')
.child(widget.threadId)
.child(_fileName);
final uploadTask = storeRef.putFile(widget.file);
uploadTask.events.listen((event) {
setState(() {
_uploadPercentage = event.snapshot.bytesTransferred.toDouble() /
event.snapshot.totalByteCount.toDouble();
});
print(_uploadPercentage);
});
uploadTask.onComplete.then((snapshot) {
setState(() {
_uploadStatus = UploadProgressStatus.complete;
});
});
}
Here is a demo GIF of this issue:
GIF image demo
Any suggestions/solutions would be appreciated.
Thank you
Null the file value after image uploading task might work.
like
widget.file=null
place it after uploadTask
Related
so, I'm new to react native and firebase storage and I am trying to upload images to storage which fails the first time but if I try immediately afterwards it works with no issues at all and can see the photo in the storage.
The error I am getting on the first attempt is:
{"code":"storage/unknown","customData":{},"name":"FirebaseError","status_":400,"_baseMessage":"Firebase Storage: An unknown error occurred, please check the error payload for server response. (storage/unknown)","line":20330,"column":28,"sourceURL":"http://192.168.1.12:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&strict=false&minify=false"}
This is the code below:
const submitPhoto = async () => {
let id = await auth.currentUser.uid;
// Create file metadata including the user id
var metadata = {
customMetadata: {
userUpload: id,
},
};
var ref;
try {
const response = await fetch(image);
const blob = await response.blob();
let imageName = image.substring(image.lastIndexOf('/') + 1);
setFileName(imageName);
ref = await firebase
.storage()
.ref('climbPhotos')
.child(filename)
.put(blob, metadata);
console.log(ref.metadata.name);
console.log(ref.metadata);
} catch (error) {
console.log(JSON.stringify(error));
Alert.alert('Server Error', 'Error has occurred on image upload');
}
setImage(null);
};
I am at a lost with this as like I said it occurs only on the first attempt of me opening that particular screen and uploading any image for the first time.
So I figured out what the issue was, and it's all to do with the upload using state values for the filename apparently even though the value was being set (i checked with console logs immediately after being set) the value wasn't always being passed correctly. whether this was the issue or not it seems my issue has been solved by removing states from the child.
const response = await fetch(image);
const blob = await response.blob();
let imageName = image.substring(image.lastIndexOf('/') + 1);
setFileName(imageName);
console.log(' vvvv ');
console.log(filename);
console.log(imageName);
console.log(image);
console.log(' ^^^ ');
var imageRef = firebase.storage().ref('climbPhotos').child(imageName);
26a51e0a-8ab8-4b62-aa4a-46a3d777e4fa.jpeg
file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FRockClimbingApp-cde1c400-dff9-44c8-8840-c83b731c56de/ImagePicker/26a51e0a-8ab8-4b62-aa4a-46a3d777e4fa.jpeg
as you can see the log shows the image and imageName but not the filename due to states being async
I am migrating one of my apps to the web with flutter for the first time. My app uses firebase packages to post images to firebase storage. We know "file path" doesn't work on the web so I had to change to byte. But on the firebase storage, I discovered that the images posted through bytes cannot be previewed. If you click on the token, rather than a preview, it will just download straight away.
Here is a pictorial illustration of what I mean:
Images, when clicked are supposed to be able to show a preview like this
But this is what I get
I see they are uploaded as documents instead of file.
Please how do I rectify this.
This is my code below
Future selectFile() async {
final result = await FilePicker.platform.pickFiles(allowMultiple: false, type: FileType.image);
if (result == null) return;
final path = result.files.single;
setState(() => imageFile = path);
}
//upload
Future upload() async {
if (imageFile == null) return;
final fileName = imageFile!.name;
final destination = 'FTV/Thumbnails/$fileName';
// task = FirebaseApi.uploadFile(destination, file!);
task = FirebaseApi.uploadBytes(destination, fileBytes!);
setState(() {});
if (task == null) return;
final snapshot = await task!.whenComplete(() {});
var urlDownload = await snapshot.ref.getDownloadURL();
// print('Download-Link: $urlDownload');
setState(() {
imageURL = urlDownload;
});
}
I'm enrolled in a project using Flutter and Firebase and I'm having trouble with bandwidth limits. The free quota is 1gb per day and I have a list with 100 images (and some files).
Is there a way to minimize the bandwidth costs through caching this files in local phone cache to not have to get the items from DB each time I open the screen?
Is there a package or something like this to do it?
I think you can do it easily with cached network image
If you want more control, I created a simple function to do this job, you can customize it further according to your needs:
import 'dart:typed_data';
import 'dart:io';
import 'package:http/http.dart' show get;
Future<File> getCachedImageFile(final String imageId) async {
final Directory temp = await getTemporaryDirectory();
final String fullPathName = '${temp.path}/images/$imageId';
final File imageFile = File(fullPathName);
if (imageId == null) return null;
if (await imageFile.exists() == false) { // will guarantee that you don't make the API request (or just get image from url if unprotected)
final String endpoint = 'http://www.mywebsiteorRESTapi.com/'
String imgUrl = endpoint + imageId + '.png';
var response = await get(imgUrl);
try {
await imageFile.create(recursive: true);
if (response.bodyBytes != null)
await imageFile.writeAsBytes(response.bodyBytes);
return imageFile;
} on Exception catch (exception) {
throw 'could not write image $exception';
}
}
return imageFile;
}
In your FutureBuilder:
future: getCachedImageFile('1VsSbB4Kh7Ab7spjQBA');
...
return Image.file(snapshot.data)
You can use CachedNetworkImage package to avoid downloading the image every time. It's simple to use and you just need to pass the URL to the Widget:
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
To control how long the image is cached*, make sure you add cache header to your images when you upload them so they get cached properly (in the browser too if you're using flutter web):
final contentType = 'image/*';
final cacheControl = 'public, max-age=31556926'; // seconds -- ie 31556926 == one year
final uploadTask = reference.putData(
image.data,
SettableMetadata(
cacheControl: cacheControl,
contentType: contentType,
));
So make sure to store the URL of the images when you upload them and just pass the URL to the users to get the images instead of downloading the images directly from FirebaseStorage in case you're doing that.
*I believe the package default is 7 days if no cache header is available but I cannot confirm.
This is my first experience with Flutter, just trying to create a super simple app that uploads a bunch of photos to firebase-storage to assist our media & marketing teams internally.
I have successfully implemented multi_image_picker, I can select images and build out the grid view, the images are stored in a list.
I have an upload button, when pressed it calls a uploadImages Function which should loop over the list of images created by multi_image_picker and call the saveImage function to save the images to Firebase.
While it triggers, it only ever uploads a single image and never the entire list as such, the for looop never seems to keep running.
Button to Call uploadImages function:
RaisedButton(
child: Text("Upload Images"),
onPressed: () {
uploadImages(images);
},),
uploadImages function with for loop:
void uploadImages(List<Asset> images) async {
for (var i = 0; i < images.length; i++) {
await saveImage(images[i]);
}
}
Finally the saveImage function that works, but only ever uploads a single image:
Future saveImage(Asset i) async {
ByteData byteData = await i.getByteData();
List<int> imageData = byteData.buffer.asUint8List();
StorageReference ref =
FirebaseStorage.instance.ref().child("$now " + "$displayName.jpg");
StorageUploadTask uploadTask = ref.putData(imageData);
StorageTaskSnapshot storageSnapshot = await uploadTask.onComplete;
var downloadURL = await storageSnapshot.ref.getDownloadURL();
if (uploadTask.isComplete) {
final String url = downloadURL.toString();
return url;
} else {
print('Error from image repo ${storageSnapshot.error.toString()}');
}}
I've tried to many different things, found a few errors with debugger and resolved, but after days of trying I am just unable to get it to keep uploading the images per the for loop. Can someone see what I have done wrong here?
I'm not sure this is flutter or Firebase Storage.
I captured video with my iPhone portrait mode and uploaded to Firebase Storage. When I play that video, it gets rotated. So it looks wired like video is stretched to the screen. I guess it looks ok if you see it in landscape mode but since I disable my app to rotate, there is no way to see it correct aspect ratio.
I upload video and play it like so.
Future _upload(File file) async {
try {
String now = DateTime.now().millisecondsSinceEpoch.toString();
String storageId = (now + uid);
String contentType = 'video/mp4';
StorageReference ref = FirebaseStorage.instance.ref().child("video").child(storageId);
StorageUploadTask uploadTask = ref.putFile(file, StorageMetadata(contentType: contentType));
Uri downloadUrl = (await uploadTask.future).downloadUrl;
String url = downloadUrl.toString();
print(url);
} catch (error) {
print(error);
}
}
VideoPlayerController _controller;
new AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: new VideoPlayer(_controller)
)
Does anyone know how to fix this?
This seems to be this known issue https://github.com/flutter/flutter/issues/17606
with a pending pull request https://github.com/flutter/flutter/issues/17606#issuecomment-415621941
Please upvote the issue to increase priority.