Flutter web - Upload Image File to Firebase Storage
Asked Answered
M

8

21

On flutter web, I pick an image file from the computer and get a File image object. Then I want to upload it to Firebase Storage. For Android and iOS versions of the app I was using a Firebase Cloud Function and an http multipart request. It worked, but for the web version of the app it doesn't. So,

How can I upload an html image file to Firebase Storage, either directly or through a Cloud Function?

Mohur answered 13/1, 2020 at 12:46 Comment(2)
I think you should be able to use Firebase Cloud Storage for Flutter # pub.dev/packages/firebase_storageDelldella
@RenaudTarnec That is the FlutterFire plugin, which only works for Android and iOS. For Web, use firebase-dart: github.com/FirebaseExtended/firebase-dart/tree/master/example/…Trichloride
M
24

Finally I managed to find a solution to this issue. To achieve this I needed to install two dependencies, firebase and universal_html. Yet difficult to find out the solution, its implementation was actually simple. Here is the code of the function I used to upload the html image file to Firebase Storage, into the "images" folder:

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

Future<Uri> uploadImageFile(html.File image,
      {String imageName}) async {
    fb.StorageReference storageRef = fb.storage().ref('images/$imageName');
    fb.UploadTaskSnapshot uploadTaskSnapshot = await storageRef.put(image).future;
    
    Uri imageUri = await uploadTaskSnapshot.ref.getDownloadURL();
    return imageUri;
}

I hope it helps someone with the same need as me.

Mohur answered 15/1, 2020 at 8:54 Comment(7)
upload was success but not open. header?Macey
How do you get the html.File?Obligate
@Macey I got the same problem, file corrupted. What should I do?Metternich
You made my day! Thanks for the answerKillerdiller
Why import that universal_html library, when you can just use import 'dart:html' as html;?Hainaut
Going of this answer, what worked for me is using image_picker_for_web to get the image. Then turning that image into base64 encoding and using putString instead of put like the above. More documentation on putString can be found hereLn
Hi Prince Hodonou, can you please post an example thanks in advance!Purser
B
20

After Combining so many posts I did it, and it works!

No, you just don't need any Kind of Universal_HTML or another image_picker_web. Just stick with Image Picker(https://pub.dev/packages/image_picker). And use the below code as I have used to upload the Image to Firebase Storage, and it works in all the way IOS, Android, Web, I hope you've already added the permission for ios and android. Let's Begin!

Import

import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as Path;

Call this method when you want to open a file picker in any of the above platforms!

chooseImage() async {
PickedFile? pickedFile = await ImagePicker().getImage(
      source: ImageSource.gallery,
    );
}

now you've file in pickedFile use kIsWeb to find out if it's web or not!

uploadImageToStorage(PickedFile? pickedFile) async {
if(kIsWeb){
Reference _reference = _firebaseStorage
        .ref()
        .child('images/${Path.basename(pickedFile!.path)}');
    await _reference
        .putData(
      await pickedFile!.readAsBytes(),
      SettableMetadata(contentType: 'image/jpeg'),
    )
        .whenComplete(() async {
      await _reference.getDownloadURL().then((value) {
        uploadedPhotoUrl = value;
      });
    });
 }else{
//write a code for android or ios
}

}
Beach answered 29/6, 2021 at 20:10 Comment(1)
This library only works for iOS and Android. Web, which the OP is asking about, is not supported.Tillion
G
9

Answer for 2022

TLDR: Convert your files to XFiles using the cross_file package before uploading.

Select an Image

Use the image_picker package to select an image. This works on Android, iOS, and the web.

Future<String?> selectPicture(ImageSource source) async {
  XFile? image = await imagePicker.pickImage(
    source: source,
    maxHeight: 1000,
    maxWidth: 1000,
  );

  return image?.path;
}

Convert the XFile to a Uint8List

String path = selectPicture(ImageSource.gallery);
Uint8List imageData = await XFile(path).readAsBytes()

Upload the Uint8List to Cloud Storage

UploadTask uploadTask = storageReference.putData(imageData);
Gormand answered 22/3, 2022 at 13:31 Comment(1)
worked well, thank you @Joe Muller. is there a typo in your answer? 'storageReference.putData(file);' should be 'storageReference.putData(imageData);'. for newbies (like me), you may need "import 'dart:typed_data';" to handle Uint8List.Henka
P
6

Here is the complete snip that work for me for uploading image: html.File doesn't work for me, the file is uploaded but you will see Error loading preview in firebase storage, so just passing the bytes directly work for me.

To show an image you can use mediaInfo.bytes with widget that support bytes e.g FadeInImage you can use MemoryImage(mediaInfo.bytes) and Image.memory(mediaInfo.bytes)

packages used:

  1. firebase
  2. image_picker_web
  3. path
  4. mime_type
    Future<MediaInfo> imagePicker() async {    
        MediaInfo mediaInfo = await ImagePickerWeb.getImageInfo;
        return mediaInfo;
     }
     
     Future<Uri> uploadFile(
          MediaInfo mediaInfo, String ref, String fileName) async {
        try {
          String mimeType = mime(Path.basename(mediaInfo.fileName));

          // html.File mediaFile =
          //     new html.File(mediaInfo.data, mediaInfo.fileName, {'type': mimeType}); 
          final String extension = extensionFromMime(mimeType);

          var metadata = fb.UploadMetadata(
            contentType: mimeType,
          );

          fb.StorageReference storageReference =
              fb.storage().ref(ref).child(fileName + ".$extension");

          fb.UploadTaskSnapshot uploadTaskSnapshot =
              await storageReference.put(mediaInfo.data, metadata).future;

          Uri imageUri = await uploadTaskSnapshot.ref.getDownloadURL();
          print("download url $imageUri");
          return imageUri;
        } catch (e) {
          print("File Upload Error $e");
          return null;
        }
      }

Photophore answered 5/7, 2020 at 13:37 Comment(0)
T
3

To access Cloud Storage in your Flutter for Web application, you can use the firebase-dart plugin. You can find an example of accessing storage through firebase-dart in the repo.

Trichloride answered 13/1, 2020 at 15:47 Comment(2)
github.com/FirebaseExtended/firebase-dart/tree/master/example/… is not availableRrhoea
It now seems to be here: github.com/FirebaseExtended/firebase-dart/tree/master/firebase/…Trichloride
V
3

Select an Image

Use the image_picker package to select an image.

Future<void> imgFromGallery() async {

    final pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery);
    
    Uint8List imageData = await XFile(pickedFile!.path).readAsBytes();

    uploadImage(imageData );
}

Upload to Cloud Storage

Use the UUID package to create a unique name.

 Future<String> uploadImage(Uint8List xfile) async {

    Reference ref = _storage.ref().child('Folder');
    String id = const Uuid().v1();
    ref = ref.child(id);

    UploadTask uploadTask = ref.putData(
      xfile,
      SettableMetadata(contentType: 'image/png'),
    );
    TaskSnapshot snapshot = await uploadTask;
    String downloadUrl = await snapshot.ref.getDownloadURL();
    return downloadUrl;
  }

Upload to Cloud Firestore

  Future<void> addData() async {
    try {
      await FirebaseFirestore.instance.('DBname').add({'image':downloadUrl});
      
    } on FirebaseException catch (e) {
     
    } catch (_) {
      
    }
  }

Show image in the flutter Web App

  1. Open the Google cloud console, select your project and start a cloud terminal session by clicking the >_ icon button in the top navbar.
  2. Click the open editor button, then create the cors.json file and enter the code below and save it.
[
  {
    "origin": ["*"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]
  1. Open Terminal (Cloud Shell) and run the below code.
 gsutil cors set cors.json gs://your-bucket

Note: replace your bucket id from firebase Storage

  1. Now make the Ui in the Flutter Web App
Image.network(widget.project.image!)

Note: Only if you have already set the following codes

web/index.html

<HTML>
....
<body>
<script type="module">

    import { initializeApp } from "https://www.gstatic.com/firebasejs/9.14.0/firebase-app.js";
    import { initializeApp } from "https://www.gstatic.com/firebasejs/9.14.0/firebase-storage.js";
    import { initializeApp } from "https://www.gstatic.com/firebasejs/9.14.0/firebase-firestore.js";
 
const firebaseConfig = {
    apiKey: "....",
    authDomain: "...",
    projected: "....",
    storageBucket: "...",
    messagingSenderId: "....",
    appId: "....",
    measurementId: "...."
  };
  
    const app = initializeApp(firebaseConfig);
  </script>
....
</body>
</html>
Vaseline answered 30/11, 2022 at 7:57 Comment(0)
M
1
 void uploadImage({required Function(File? file) onSelected}) {
    var uploadInput = FileUploadInputElement()..accept = 'image/*';
    uploadInput.click();

    uploadInput.onChange.listen((event) async {
      final file = uploadInput.files!.first;

      final reader = FileReader();
      reader.readAsDataUrl(file);
      reader.onLoadEnd.listen((event) async {
        onSelected(file);
      });
    });
  }

  void uploadToStorage() {
    final dateTime = DateTime.now();
    final userId = FirebaseAuth.instance.currentUser!.uid;
    imagePath = '$userId/$dateTime'.replaceAll(' ', '');

    uploadImage(onSelected: (file) {
      try {
        fb.storage().refFromURL('{reference url from firebase}').child(imagePath).put(file);
      } catch (e) {
        print('uploadImage $e');
      }
    });
  }

Call uploadToStorage function on button click and to show the image,

 Future<Uri> downloadImageUrl(String? path) {
    print(
        'downloadImageUrl:: ${fb.storage().refFromURL('{reference url from firebase}').child(path!).getDownloadURL()}');
    return fb
        .storage()
        .refFromURL('gs://onehourappbuildpractice.appspot.com/')
        .child(path)
        .getDownloadURL();
  }

FutureBuilder<Uri>(
                      future: downloadImageUrl(
                          controller.hiddenGemList[i].imagePath!),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return Center(
                            child: CircularProgressIndicator(),
                          );
                        }
                        return Container(
                            height: 100,
                            width: 200,
                            child: FadeInImage.assetNetwork(
                              image: snapshot.data.toString(),
                              placeholder: 'assets/placeholder_image.jpeg',
                            ));
                      })
Malefic answered 29/7, 2021 at 0:9 Comment(1)
After many attempts, this was the one that worked. ThanksPotvaliant
S
0

Adding on to @WebRooseDevelopment you may also need to update the index.html file to include the new firebase versions.

'src="https://www.gstatic.com/firebasejs/8.6.1/firebase-storage.js">'

Systole answered 30/6, 2021 at 13:28 Comment(1)
(quoting and decoration are not properly nested)Commitment

© 2022 - 2024 — McMap. All rights reserved.