繁体   English   中英

Flutter Web 上传到 Firestore

[英]Flutter Web Upload to Firestore

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

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

这是我所做的:

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

转换Uint8ListFile使用dart:html 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);

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

我没有尝试过你提到的替代方案,但下面在 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); 
}

对于以下基本问题:

“如何将图像字节上传到 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 的图像小部件。

这是一篇旧帖子,但以防万一有人仍然需要帮助,因为我已经搜索了几个小时来解决这个问题。 这就是我的做法。

  1. 导入image_picker_web 我使用的是 2.0.3 版。
  2. 在按钮监听器上使用 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页面上有更多关于此以及如何选择多个图像的信息 您也可以使用此处的概念使其与 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,
        )
      ],
    );
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM