【问题标题】:Flutter Web Upload to FirestoreFlutter Web 上传到 Firestore
【发布时间】:2020-06-28 09:03:20
【问题描述】:

我在使用 Flutter Web 并将图像上传到 Firestore 时遇到问题。我很确定问题出在图像选择器上,因为普通(移动)image picker 不适用于网络。正常的图像选择器返回一个文件,但替代的 image_picker_web 返回一个图像,它在上传时被拒绝,因为它需要一个 Future<File>

image_picker_web 有一个替代方法来返回我使用过的Uint8List,然后通过dart:html 转换为File - 并且上传正常,但图像已损坏且无法查看。

这是我所做的:

按下按钮 - 选择图像为Uint8List > 转换为Image,存储在内存中并在屏幕上显示

                  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;
                    });
                  },

使用dart:html FileUint8List 转换为File 并命名为用户UID.png(已上传PNG)

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

使用方法上传文件

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;
  }

调用方法

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

将包括位置在内的数据添加到 Firebase 数据库

//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);

一切正常,只是图像似乎已损坏,它也以几乎两倍的文件大小返回,这显然意味着文件没有像图像一样返回..

【问题讨论】:

标签: firebase flutter google-cloud-firestore flutter-web


【解决方案1】:

我还没有尝试过您提到的替代方案,但以下在 Flutter web 和 Firebase 上对我有用。 uploadInput 的事件监听器适用于大多数平台。关于 document.body.append 的最后一部分将确保它也适用于 Mobile safari。

  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();
  }

然后上传到 Firebase 存储:

uploadToFirebase(String imageName, File file) async {
 Firebase.UploadTask task = storage.refFromURL('gs://.../images/' + imageName).put(file); 
}

【讨论】:

    【解决方案2】:

    针对以下基本问题:

    "如何将图像字节上传到 Firebase 存储?"

    这是一个可能的实现:

    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;
    }
    

    结果 (snapshot.ref.getDownloadURL()) 是一个合格的 URL,您可以将其与任何加载 URL 的图像小部件一起使用。

    【讨论】:

      【解决方案3】:

      这是一篇旧帖子,但如果有人仍然需要帮助,因为我一直在寻找几个小时来解决这个问题。我就是这样做的。

      1. 导入image_picker_web。我使用的是 2.0.3 版。
      2. 在按钮 ontap 侦听器上使用 ImagePickerWeb.getImageInfo 来获取图像信息。
      var fileInfo = await ImagePickerWeb.getImageInfo;
      
      1. 在小部件树中使用 Image.memory 显示图像。 (可选)
      Image.memory(fileInfo.data!,width: 180),
      
      1. 创建 Firebase 上传位置
      final firebasefileLocation = firebaseStorageLocation.child('${DateTime.now()}_${fireInfo.fileName}');
      
      1. 将图片上传到 Firebase。
       await firebasefileLocation.putData(img.data!);
      

      这就是我的文件在手机和网络上的工作方式。在image_picker_web 页面上有更多关于此以及如何选择多个图像的信息。您也可以使用here 中的概念使其与IOS 和Android 跨平台。

      import 'package:firebase_storage/firebase_storage.dart';
      import 'package:flutter/foundation.dart';
      import 'package:flutter/material.dart';
      import 'package:image_picker_web/image_picker_web.dart';
      
      class ImagePickerDemo extends StatefulWidget {
        const ImagePickerDemo({Key? key}) : super(key: key);
      
        @override
        _ImagePickerDemoState createState() => _ImagePickerDemoState();
      }
      
      class _ImagePickerDemoState extends State<ImagePickerDemo> {
        MediaInfo? _imageInfo;
      
        Future<void> _pickImage() async {
          var fileInfo = await ImagePickerWeb.getImageInfo; //get image
          if (fileInfo.data == null) return; // user did not choose image.
          setState(() {
            _imageInfo = fileInfo; // save image
          });
        }
      
        Future<void> _uploadImage() async {
          if (_imageInfo == null) return;
          final firebaseStorageLocation =
              FirebaseStorage.instance.ref().child('product_images');
          final imageInfo = _imageInfo as MediaInfo;
          _imageInfo as MediaInfo;
          final firebasefileLocation = firebaseStorageLocation
              .child('${DateTime.now()}_${imageInfo.fileName!}');
      
          await firebasefileLocation.putData(imageInfo.data!);
          final urlToUseLater = await firebasefileLocation.getDownloadURL();
        }
      
        @override
        Widget build(BuildContext context) {
          return Column(
            children: [
              ElevatedButton(onPressed: _pickImage, child: Text('Choose Image')),
              ElevatedButton(
                  onPressed: _imageInfo == null ? null : _uploadImage,
                  child: Text('Upload Image')),
              Image.memory(
                _imageInfo!.data!,
                width: 180,
              )
            ],
          );
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-08-20
        • 2018-12-05
        • 2021-05-13
        • 2021-03-29
        • 2020-07-07
        • 2020-03-12
        • 2022-11-16
        • 2020-09-24
        相关资源
        最近更新 更多