Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Web Upload to Firestore

I am having issues with Flutter web and uploading Images to Firestore. I'm pretty sure the issue lies in the Image Picker, as the normal(mobile) image picker does not work for the web. The normal image picker returns a File, but the alternative image_picker_web returns an Image, which gets rejected on upload because it's expecting a Future<File>.

image_picker_web has an alternative to return a Uint8List which I have used, and then converted to a File via dart:html - and uploads fine, but the image is corrupted and not viewable.

Here's what I have done:

On Button Press - Pick Image as Uint8List > Convert to Image, Store in memory and Display on Screen

                  onPressed: () async {
                    //Upload Image as Uint8List
                    imageBytes = await ImagePickerWeb.getImage(asUint8List: true);
                    //Convert Uint8List to Image
                    _image = Image.memory(imageBytes);
                    //Show new image on screen
                    setBottomSheetState(() {
                      image = _image;
                    });
                  },

Convert Uint8List to File using dart:html File and name as users UID.png (PNG Uploaded)

 imageFile = html.File(imageBytes, '${user.uid}.png');

Use Method to upload File

import 'dart:async';
import 'package:firebase/firebase.dart' as fb;
import 'package:universal_html/prefer_universal/html.dart' as html;

String url;

  Future<String> uploadProfilePhoto(html.File image, {String imageName}) async {

    try {
      //Upload Profile Photo
      fb.StorageReference _storage = fb.storage().ref('profilephotos/$imageName.png');
      fb.UploadTaskSnapshot uploadTaskSnapshot = await _storage.put(image).future;
      // Wait until the file is uploaded then store the download url
      var imageUri = await uploadTaskSnapshot.ref.getDownloadURL();
      url = imageUri.toString();

    } catch (e) {
      print(e);
    }
    return url;
  }

Call method

location = await uploadProfilePhoto(imageFile, imageName: '${user.uid}');

Add data including Location to Firebase Database

//Pass new user ID through to users Collection to link UserData to this user
await AdminUserData(uid: user.uid).updateAdminUserData(name: userName, email: userEmail, profilephoto: location);

Everything is working OK, just the image seems to be corrupted, it also comes back at almost double the filesize, which obviously means the File isn't coming back as the Image..

like image 681
JobsyNZ Avatar asked Mar 17 '20 09:03

JobsyNZ


People also ask

How do I add data to firestore in Flutter?

To use Flutter with Firebase, you will first need to set dependencies in the pubspec file. You will also have to import firestore , i.e., the database provided by Firebase for data handling. Now, import the Firebase dependencies into your Dart file. import 'package:cloud_firestore/cloud_firestore.

How do you upload pictures to firestore in Flutter?

The idea is to create a simple interface containing an image display container and a button to upload an image after selecting it from the gallery. After an image is selected, the file will be automatically uploaded to the Firebase storage and then displayed on the screen.

How do I upload a PDF to firestore Flutter?

You can use this dependency https://pub.dev/packages/file_picker to upload any kind of file to database. By this you can upload file to firebase storage and insert the url to realtime database.


2 Answers

I have not tried the alternatives you mentioned, but below has worked for me before on Flutter web and Firebase. The event listener for uploadInput works for most platforms. The last part regarding document.body.append will ensure that it works on Mobile safari as well.

  Future<void> _setImage() async {
    final completer = Completer<String>();
    InputElement uploadInput = FileUploadInputElement();
    uploadInput.multiple = false;
    uploadInput.accept = 'image/*';
    uploadInput.click();
    
    uploadInput.addEventListener('change', (e) async {
      // read file content as dataURL
      final files = uploadInput.files;
      Iterable<Future<String>> resultsFutures = files.map((file) {
        final reader = FileReader();
        reader.readAsDataUrl(file);
        reader.onError.listen((error) => completer.completeError(error));
        return reader.onLoad.first.then((_) => reader.result as String);
      });

      final results = await Future.wait(resultsFutures);
      completer.complete(results[0]);
    });

    
    document.body.append(uploadInput);
    final String image = await completer.future;

    widget.newImage = uploadInput.files[0];

    // Upload to Firebase
    uploadToFirebase(widget.newImage); // This is dart:html File

    uploadInput.remove();
  }

Then the upload to Firebase Storage:

uploadToFirebase(String imageName, File file) async {
 Firebase.UploadTask task = storage.refFromURL('gs://.../images/' + imageName).put(file); 
}
like image 105
FriedRyan Avatar answered Sep 25 '22 19:09

FriedRyan


For the underlying question of:

"How to upload image bytes to Firebase Storage?"

Here is a possible implementation:

import 'dart:developer';

import 'package:file_picker/file_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';

/// Opens a file picker and uploads a single selected file to Firebase storage.
/// Returns a download URL if upload is successful or null if the operation is 
/// aborted.
/// 
/// Throws an exception if more than one file is selected or the selected file 
/// size exceeds 300KB
Future<String?> pickAndUploadFile() async {
  final ref = FirebaseStorage.instance.refFromURL('gs://YOUR-PROJECT.appspot.com');
  String? res;
  final filePickerRes = await FilePicker.platform.pickFiles();
  if (filePickerRes != null) {
    if (filePickerRes.count == 1) {
      final file = filePickerRes.files.single;
      if (file.size > 300000) {
        throw Exception('File must be less than 300KB');
      }
      final upTask = ref.child('uploads/${file.name}').putData(file.bytes!);
      final snapshot = upTask.snapshot;
      res = (await snapshot.ref.getDownloadURL()).toString();
    } else {
      throw Exception('only one file allowed');
    }
  }
  log('downloadUrl: $res');
  return res;
}

The result (snapshot.ref.getDownloadURL()) is a qualified URL you can use with any image widget that loads a URL.

like image 27
Alex.F Avatar answered Sep 23 '22 19:09

Alex.F