【问题标题】:How to upload multiple images using Dio and multi_image_picker plugin in Flutter如何在 Flutter 中使用 Dio 和 multi_image_picker 插件上传多张图片
【发布时间】:2020-05-03 22:19:01
【问题描述】:

我想在 Flutter 中使用 Dio 和 multi_image_picker 插件上传多张图片。

List<Asset> 这是问题所在,因为我无法从 List<Asset> 转换为 List<File>,所以如果您有任何解决方案,请帮助我。

尝试使用:

  1. multi_image_picker: ^4.6.1

  2. dio: ^3.0.4

谢谢

博纳 SR。

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:merchantside/helper/colorhelper.dart';
import 'package:merchantside/merchantside/login.dart';
import 'dart:async';
import 'package:multi_image_picker/multi_image_picker.dart';

class ListImages extends StatefulWidget {
  String errorMessage = "";
  @override
  _ListImagesState createState() => new _ListImagesState();
}

class _ListImagesState extends State<ListImages> {
  List<Asset> images = List<Asset>();
  List<File> listImages = [];
  @override
  void initState() {
    super.initState();
  }

  Widget buildGridView() {
    return GridView.count(
      crossAxisCount: 3,
      children: List.generate(images.length, (index) {
        Asset asset = images[index];
        return AssetThumb(
          asset: asset,
          width: 300,
          height: 300,
        );
      }),
    );
  }


  void _uploadFiles() async {
    String uid = await FlutterSecureStorage().read(key: "getTocken");
    try {
      var dio = Dio();
      FormData formData = new FormData.fromMap({
        "pictures[]": images, 
      });
      Response resp = await dio.post(
        mainUrl + 'merchant/upload-galleries',
        data: formData,
        onSendProgress: (int sent, int total) {
          //
        }, 
        options: Options(
          headers: {
            HttpHeaders.authorizationHeader: uid,
          },
        )
      );
      if(resp.statusCode == 200) {
        print("============= Print Resp data: ");
        print(resp.data);
      }

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

  Future<void> loadAssets() async {
    List<Asset> resultList = List<Asset>();
    try {
      resultList = await MultiImagePicker.pickImages(
        maxImages: 6,
        enableCamera: true,
        selectedAssets: images,
        cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
        materialOptions: MaterialOptions(
          actionBarColor: "#abcdef",
          actionBarTitle: "Example App",
          allViewTitle: "All Photos",
          useDetailsView: false,
          selectCircleStrokeColor: "#000000",
        ),
      );
    } on Exception catch (e) {
      print(e);
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;
    setState(() {
      images = resultList;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        heroTag: "btn1",
        backgroundColor: ColorHelper.orange,
        child: Icon(Icons.add_photo_alternate),
        onPressed: loadAssets,
      ),
      appBar: new AppBar(
        title: Text('បញ្ជីរូបភាព'),
        backgroundColor: ColorHelper.orange,
      ),
      body: Column(
        children: <Widget>[
          //Error message
          errorMessage != "" ? 
          Container(
            margin: EdgeInsets.only(left: 10, right: 10, top: 10),
            height: 50,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.all(Radius.circular(4)),
              color: ColorHelper.red.withOpacity(0.5),
            ),
            child: Center(
              child: Text("$errorMessage", style: TextStyle(color: ColorHelper.swhite, fontSize: 15),),
            ),
          ):
          Container(),

          Expanded(
            child: Container(
              margin: EdgeInsets.only(left: 10, right: 10, top: 10),
              child: buildGridView(),
            ),
          ),
          SafeArea(
            child: Container(
              margin: EdgeInsets.all(10),
              decoration: BoxDecoration(
                color: ColorHelper.green,
                borderRadius: BorderRadius.all(Radius.circular(4))
              ),
              height: 50,
              child: InkWell(
                onTap: () {
                  if(images.length > 0) {
                    setState(() {
                      errorMessage = "";
                    });
                    // Call function upload multiple files
                    _uploadFiles();
                  } else {
                    setState(() {
                      errorMessage = "សូមបញ្ជូលរូបភាព";
                    });
                  } 
                },
                child: Center(
                  child: Text("រួចរាល់", style: TextStyle(color: ColorHelper.swhite, fontSize: 15, fontWeight: FontWeight.w500,),),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

【问题讨论】:

  • 欢迎来到 Stackoverflow!这看起来是一个合理的问题 - 但如果您发布您尝试过的代码,它可能会有所帮助。
  • 你好亲爱的,现在我准备更新我的源代码......请帮助我!谢谢!
  • 您可以使用file_picker 并直接获取选择的文件列表。它支持多选并提供文件路径作为响应。这将是最简单的方法。

标签: android ios flutter mobile dart


【解决方案1】:
   FormData formData = FormData.fromMap({
      'id': 'ZE8980',
      'name': 'myTitle',
      'age':'22',
      'order_images': [
        for (var file in images)

         // ....SPRED images

        ...{
          await MultipartFile.fromFile(getImage(file).imageFile.path,
              filename: getImage(file).imageFile.path.split('/').last)
        }.toList()
      ]
    });
    Dio dio = new Dio();
    var response = await dio.post(url, data: formData);

【讨论】:

    【解决方案2】:

    简单有效:

    Map<String, dynamic> params = Map();
    params['images'] = null;
    Map<String, dynamic> headers = {
      HttpHeaders.contentTypeHeader: 'multipart/form-data',
    };
    List<MultipartFile> multipart = List<MultipartFile>();
    
    //listImage is your list assets.
    for (int i = 0; i < listImage.length; i++) {
      ByteData byteData = await listImage[i].getByteData();
      List<int> imageData = byteData.buffer.asUint8List();
      print("name: ${listImage[i].name}");
      String fileName = "${listImage[i].name}";
      multipart.add(MultipartFile.fromBytes(imageData, filename: fileName, contentType: MediaType("image", "jpg"),));
    }
    if (multipart.isNotEmpty){
      params['images'] = multipart;
    }
    
    FormData formData = new FormData.fromMap(params);
    

    【讨论】:

      【解决方案3】:

      您不需要将 Asset 转换为 File,您可以将 is 作为字节数组发送。

      只需要创建每个图像的Multipart对象。

      可以从 Assets 中获取字节数组

       ByteData byteData = await asset.getByteData();
        List<int> imageData = byteData.buffer.asUint8List();
      

      那么就可以通过MultipartFile.fromBytes()方法了。

      它看起来像,

        String url = "upload/url";
      
                List<Asset> images = List<Asset>();
                List<MultipartFile> multipartImageList = new List<MultipartFile>();
                if (null != images) {
                  for (Asset asset in images) {
                    ByteData byteData = await asset.getByteData();
                    List<int> imageData = byteData.buffer.asUint8List();
                    MultipartFile multipartFile = new MultipartFile.fromBytes(
                      imageData,
                      filename: 'load_image',
                      contentType: MediaType("image", "jpg"),
                    );
                    multipartImageList.add(multipartFile);
                  }
      
                  FormData formData = FormData.fromMap({
                    "multipartFiles": multipartImageList,
                    "userId": '1'
                  });
      
                  Dio dio = new Dio();
                  var response = await dio.post(url, data: formData);
                }
      

      FYI

      【讨论】:

        【解决方案4】:

        我在我的应用程序中使用了 dio 和 multi_image 插件并且它工作正常。我正在做的是在这里给我的代码 sn-ps。您可以说这是工作版本,应该可以在任何应用程序中使用。

        集团类

        final imageController = BehaviorSubject<List<Asset>>();
        StreamSink<List<Asset>> get sinkImages =>
          imageController.sink;
        Stream<List<Asset>> get getImages => imageController.stream;
        
        Future<void> loadImages(imageCount) async {
        List<Asset> imageList = List<Asset>();
        String error = 'No Error Dectected';
        
        try {
          imageList = await MultiImagePicker.pickImages(
            maxImages: imageCount,
            enableCamera: true,
            cupertinoOptions: CupertinoOptions(
              takePhotoIcon: "chat",
            ),
            materialOptions: MaterialOptions(
              actionBarColor: "#0A73B1",
              statusBarColor: "#0A73B1",
              actionBarTitle: "Select Image",
              allViewTitle: "All Photos",
              useDetailsView: false,
              selectCircleStrokeColor: "#000000",
            ),
          );
        } on Exception catch (e) {
          error = e.toString();
          print(error);
        }
        
        _error = error;
        imageController.add(imageList);
        }
        

        //发起api调用时调用该方法

        Future<GenericResponse> uploadImageRequest() async {
              RequestModel reqModel = RequestModel(
                             uploadImageList: getImages);
        
              final SharedPref prefs = SharedPref();
              String token = await prefs.readString(accessToken);
        
              GenericResponse response = await _application.repository
                  .uploadRequest(reqModel: reqModel, accessToken: token);
        
              return response;
            }
        

        所以你已经得到了你的图片列表。并将这些列表设置到您的请求模型类中。现在让我们使用 dio 将这些图像作为多部分发送。

        API 类方法

        //从存储库类调用

        Future<GenericResponse> uploadRequest(
          {RequestModel reqModel, String accessToken}) async {
        return await _apiProvider.uploadRequest(
            reqModel: reqModel, accessToken: accessToken);
        }
        

        //上传图片api请求

        Future<ResponseModel> uploadRequest(
          {RequestModel requestModel, String accessToken}) async {
        
        List<MultipartFile> imageList = [];
        
        imageList = await getMultiPartImages(requestModel.imageList);
        
        FormData formData = FormData.fromMap(RequestModel(
                uploadImageList: imageList,
            .toJson());
        
        try {
          Response response = await getDio().post(
            'api/endpoint',
            options: Options(headers: {
              'Authorization': 'Bearer $accessToken',
            }),
            data: formData,
          );
          return ResponseModel.fromJson(response.data);
        } catch (error, stacktrace) {
          debugPrint("Exception occured: $error stackTrace: $stacktrace");
          return ResponseModel.withError(handleError(error));
         }
        }
        

        //podo模型类

        class RequestModel {
         var uploadImageList;
        
         RequestModel(
          {this.uploadImageList});
        
         RequestModel.fromJson(Map<String, dynamic> json) {
          uploadImageList = json['image_list'];
         }
        
        Map<String, dynamic> toJson() {
         final Map<String, dynamic> data = new Map<String, dynamic>();
         data['image_list'] = this.uploadImageList;
         return data;
        }
        
        @override
        String toString() {
          return toJson().toString();
         }
        }
        

        【讨论】:

          【解决方案5】:

          您可以使用file_picker 并直接获取选择的文件列表。它支持多选并提供文件路径作为响应。这将是最简单的方法。

          List<File> files = await FilePicker.getMultiFilePath(
                    type: FileType.IMAGE);
          

          【讨论】:

            【解决方案6】:

            以下是如何从Assets 列表中获取字节数组列表:

            List<Asset> images;
            ...
            
            List<ByteData> byteDataList = await Future.wait(images.map((Asset image) => image.getByteData()));
            
            List<Uint8List> byteArrayList = byteDataList.map((ByteData byteData) {
              ByteBuffer byteBuffer = byteData.buffer;
              return byteBuffer.asUint8List(byteData.offsetInBytes, byteBuffer.lengthInBytes);
            }).toList();
            

            现在,您可以使用字节数组列表来使用您选择的网络客户端构建有效负载。对于那些不使用 3rd 方网络客户端的用户,这将是一个 MultipartRequest 和多个 MultipartFile.fromBytes

            【讨论】:

              【解决方案7】:

              当你从图库中挑选图片时,调用getFileList(),然后调用set state,首先使用文件列表的全局变量,每次再次挑选图片时清除这个列表。

                List<File> listFile = List<File>();
                images = resultList;
                _error = error;
                 getFileList();
              
               void getFileList() async{
                listFile.clear();
               for(int i=0; i<images.length; i++){
                var path= await images[i].filePath;
                print(path);
                var file=await getImageFileFromAssets(path);
                print(file);
                listFile.add(file);
              }
               setState(() {
              
               });
              }
              

              getImageFileFromAsset 用于将资产转换为文件

              Future<File> getImageFileFromAsset(String path) async {
              final file = File(path);
              return file;
              

              }

              并在表单数据中使用 listFile。

              【讨论】:

              • 你好 Avinash,当我尝试用你的解决方案解决这个问题时,出现了这个问题。 var path=等待图像[i].filePath;未定义的文件路径。
              • images 是一个由选取器返回的资产列表
              • 它是asset类的一个方法 /// 请求原图文件路径 Future get filePath { return MultiImagePicker.requestFilePath(_identifier); }
              • 检查您的资产类别
              • 您拥有 Asset 列表,因此您可以轻松地从 Asset 对象中获取路径
              猜你喜欢
              • 1970-01-01
              • 2020-08-11
              • 2019-10-14
              • 2020-12-27
              • 2020-12-27
              • 2011-07-06
              • 2020-08-14
              • 2021-06-29
              • 2020-09-15
              相关资源
              最近更新 更多