如何使用 HTTP 将多张图片上传到 Flutter 中的 Rest API?

Posted

技术标签:

【中文标题】如何使用 HTTP 将多张图片上传到 Flutter 中的 Rest API?【英文标题】:How to upload multiple images to the Rest API in Flutter using HTTP? 【发布时间】:2020-07-08 11:22:20 【问题描述】:

我想将多张图片上传到 Rest API。我尝试使用以下代码将单个图像上传到其余 API。 很好,对于多张图片选择,我使用的是multi_image_pickerlink,如何修改下面的代码来上传多张图片?谢谢

Future<String> uploadSingleImage(File file,String userid) async
  

    final prefs = await SharedPreferences.getInstance();
    final key = 'token';
    final value = prefs.get(key ) ?? 0;

    String fileName = file.path.split("/").last;
    var stream =
    new http.ByteStream(DelegatingStream.typed(file.openRead()));

    // get file length

    var length = await file.length(); //imageFile is your image file
    Map<String, String> headers = 
      "Accept": "application/json",
      "Authorization": "Bearer $value"
    ; // ignore this headers if there is no authentication

    // string to uri
    var uri = Uri.parse(serverUrl + "/api/v1/upload_parent_image");

    // create multipart request
    var request = new http.MultipartRequest("POST", uri);

    // multipart that takes file
    var multipartFileSign = new http.MultipartFile('photo',
        stream,
        length,
        filename: fileName
    );

    // add file to multipart
    request.files.add(multipartFileSign);

    //add headers
    request.headers.addAll(headers);

    //adding params
    request.fields['id'] = userid;
   // request.fields['firstName'] = 'abc';
    // request.fields['lastName'] = 'efg';

    // send
    var response = await request.send();

    print(response.statusCode);

    // listen for response
    response.stream.transform(utf8.decoder).listen((value) 
      print(value);
    );
  

【问题讨论】:

插件返回资产,你是如何将你的从资产转换为文件的? 【参考方案1】:

您可以将文件列表传递给您的方法,循环构建每个 MultipartFile 对象并将它们添加到您的 MultipartRequest

Future<String> uploadMultipleImage(List<File> files, String userid) async 
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;

// string to uri
var uri = Uri.parse(serverUrl + "/api/v1/upload_parent_image");

// create multipart request
var request = new http.MultipartRequest("POST", uri);

for (var file in files) 
    String fileName = file.path.split("/").last;
    var stream = new http.ByteStream(DelegatingStream.typed(file.openRead()));

    // get file length

    var length = await file.length(); //imageFile is your image file

    // multipart that takes file
    var multipartFileSign = new http.MultipartFile('photo', stream, length, filename: fileName);

    request.files.add(multipartFileSign);


Map<String, String> headers = 
    "Accept": "application/json",
    "Authorization": "Bearer $value"
; // ignore this headers if there is no authentication

//add headers
request.headers.addAll(headers);

//adding params
request.fields['id'] = userid;
// request.fields['firstName'] = 'abc';
// request.fields['lastName'] = 'efg';

// send
var response = await request.send();

print(response.statusCode);

// listen for response
response.stream.transform(utf8.decoder).listen((value) 
    print(value);
);

【讨论】:

很高兴看到这些答案我正在努力解决multiple_image_pickers 我尝试了几个库。即使大多数多图像选择器库都不能完美运行。最后,我找到了一个库(pub.dev/packages/multi_image_picker)libraries他们使用Assets,那么有没有办法将资产上传到Rest API? 你只需要将 MultiMediaPicker.pickImages 结果中的文件发送到方法,就可以正常完成 添加括号[] 以制作多个图像,如photo[]【参考方案2】:

您的图片列表

List<String> photos = ["path of image1","path of image2", "path of image3",];



 List<http.MultipartFile> newList = [];

      for (var img in photos!) 
        if (img != "") 
          var multipartFile = await http.MultipartFile.fromPath(
            'Photos',
            File(img).path,
            filename: img.split('/').last,
          );
          newList.add(multipartFile);
        
      
request.files.addAll(newList);

【讨论】:

【参考方案3】:

好吧,您几乎可以一次发送多个文件,让我发布一些代码

Future<String> uploadSingleImage(File file,File file2,String userid) async
  

    final prefs = await SharedPreferences.getInstance();
    final key = 'token';
    final value = prefs.get(key ) ?? 0;

    String fileName = file.path.split("/").last;
    var stream =
    new http.ByteStream(DelegatingStream.typed(file.openRead()));

    // get file length    
    var length = await file.length(); //imageFile is your image file
    Map<String, String> headers = 
      "Accept": "application/json",
      "Authorization": "Bearer $value"
    ; // ignore this headers if there is no authentication

    // string to uri
    var uri = Uri.parse(serverUrl + "/api/v1/upload_parent_image");

    // create multipart request
    var request = new http.MultipartRequest("POST", uri);

    // multipart that takes file
    var multipartFileSign = new http.MultipartFile('photo',
        stream,
        length,
        filename: fileName
    );

    // add file to multipart
    request.files.add(multipartFileSign);


   // Now Adding file 2 in request
   String fileName2 = file2.path.split("/").last;
    var stream2 =
    new http.ByteStream(DelegatingStream.typed(file2.openRead()));
    var lengthOfFile2 = await file2.length(); 

   // multipart that takes file
    var multipartFile2 = new http.MultipartFile('file2_key_here',
        stream2,
        lengthOfFile2,
        filename: fileName2
    );

   // add file2 to multipart
    request.files.add(multipartFile2);

    //add headers
    request.headers.addAll(headers);

    //adding params
    request.fields['id'] = userid;
   // request.fields['firstName'] = 'abc';
    // request.fields['lastName'] = 'efg';

    // send
    var response = await request.send();

    print(response.statusCode);

    // listen for response
    response.stream.transform(utf8.decoder).listen((value) 
      print(value);
    );
  

【讨论】:

【参考方案4】:

我已经分享了我用来用这个包上传多个图像的代码

  multi_image_picker: ^4.8.0 
  flutter_absolute_path: ^1.0.6 
  flutter_image_compress:
  path_provider
http

flutter compress 和 path provider 用于压缩大小

Http请求代码

static Future<String> uploadMultipleImage(List<File> files) async 
// string to uri
    var uri = Uri.parse("your api url");
    print("image upload URL - $uri");
// create multipart request
    var request = new http.MultipartRequest("POST", uri);

    for (var file in files) 
      String fileName = file.path.split("/").last;
      var stream = new http.ByteStream(DelegatingStream.typed(file.openRead()));

      // get file length

      var length = await file.length(); //imageFile is your image file
      print("File lenght - $length");
      print("fileName - $fileName");
      // multipart that takes file
      var multipartFileSign = new http.MultipartFile('images[]', stream, length,
          filename: fileName);

      request.files.add(multipartFileSign);
    

    Map<String, String> headers = 
      "Accept": "application/json",
      "Authorization":
          "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMiwiZXhwIjoxNjE3NTQyNDE0LCJpc3MiOiJsb2NhbGhvc3QiLCJpYXQiOjE2MTcxODI0MTR9.dGRbINOdx_tf417fpsjdQ5CR7uGULs98FjLGm2w4kRY"
    ; // ignore this headers if there is no authentication
    print("headers - $headers");
//add headers
    request.headers.addAll(headers);

//adding params
    request.fields['heading'] = "heading";
    request.fields['description'] = "description";
    request.fields['mobile'] = "mobile";
    request.fields['email'] = "email";
    request.fields['category'] = "1";
    request.fields['location_type'] = "1";
    request.fields['location'] = "location";
    request.fields['lat'] = "12";
    request.fields['lng'] = "123";
    request.fields['price'] = "1231";
    request.fields['sub_category'] = "3";

// send
    var response = await request.send();

    print(response.statusCode);

    var res = await http.Response.fromStream(response);
    if (response.statusCode == 200 || response.statusCode == 201) 
      print("Item form is statuscode 200");
      print(res.body);
      var responseDecode = json.decode(res.body);

      if (responseDecode['status'] == true) 
        return res.body;
       else 
        return res.body;
      
    

  

我调用该方法的 UI 屏幕

    import 'dart:convert';
    import 'dart:io';
    
    import 'package:flutter/material.dart';
    import 'package:flutter_absolute_path/flutter_absolute_path.dart';
    import 'package:flutter_image_compress/flutter_image_compress.dart';
    import 'package:lookapt_olx_app/service/ApiService.dart';
    import 'package:lookapt_olx_app/utils/Utils.dart';
    import 'package:lookapt_olx_app/utils/colorUtils.dart';
    import 'package:lookapt_olx_app/utils/fontUtils.dart';
    import 'package:lookapt_olx_app/widgets/appbar_widget.dart';
    import 'package:lookapt_olx_app/widgets/commonWidget.dart';
    import 'package:lookapt_olx_app/widgets/textFieldWidget.dart';
    import 'package:lookapt_olx_app/widgets/textWidgets.dart';
    import 'package:multi_image_picker/multi_image_picker.dart';
    import 'package:rounded_loading_button/rounded_loading_button.dart';
    
    import 'addNewPostController.dart';
    import 'multiImagePicker.dart';
    import 'package:path_provider/path_provider.dart' as path_provider;
    
    class AddNewPostScreen extends StatefulWidget 
      Map<String, dynamic> parameters;
      String categoryId;
    
      AddNewPostScreen(this.parameters, this.categoryId = "");
    
      @override
      _AddNewPostScreenState createState() => _AddNewPostScreenState();
    
    
    class _AddNewPostScreenState extends State<AddNewPostScreen> 
      @override
      void initState() 
        super.initState();
        print("add new post");
        print(widget.parameters['name']);
        print(widget.categoryId.toString());
      
    
      List<Asset> images = [];
      String _error = "";
    
      Widget buildGridView() 
        if (images != null)
          return GridView.count(
            crossAxisCount: 3,
            crossAxisSpacing: 10,
            children: List.generate(images.length, (index) 
              Asset asset = images[index];
              return AssetThumb(
                asset: asset,
                width: 300,
                height: 300,
              );
            ),
          );
        else
          return Container(color: Colors.white);
      
    
      Future<void> loadAssets() async 
        setState(() 
          images = List<Asset>();
        );
    
        List<Asset> resultList;
        String error;
    
        try 
          resultList = await MultiImagePicker.pickImages(
            maxImages: 3,
          );
         on Exception catch (e) 
          error = e.toString();
        
    
        // 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;
    
          if (error == null) _error = 'Selected images';
        );
      
    
      /*
      Usage
    
       final dir = await path_provider.getTemporaryDirectory();
    
                    final targetPath = dir.absolute.path + "/temp.jpg";
                    File imgFile = await testCompressAndGetFile(
                        File(_capturedImage.path), targetPath);
    
      * */
    
      Future<File> testCompressAndGetFile(File file, String targetPath) async 
        print("testCompressAndGetFile");
        final result = await FlutterImageCompress.compressAndGetFile(
          file.absolute.path,
          targetPath,
          quality: 30,
          minWidth: 1024,
          minHeight: 1024,
          // rotate: 90,
        );
        print(file.lengthSync());
        print(result.lengthSync());
    
        return result;
      
    
      _uploadImageFun() async 
        print("Note - _getImagePaths called");
        List<File> fileImageArray = [];
        images.forEach((imageAsset) async 
          final filePath =
              await FlutterAbsolutePath.getAbsolutePath(imageAsset.identifier);
    
          File tempFile = File(filePath);
          print(filePath);
          print("filePath.length  - $filePath.length");
          print(tempFile);
          print("tempFile.length() - $tempFile.lengthSync()");
    
          if (tempFile.existsSync()) 

          DateTime now = DateTime.now();

            final dir = await path_provider.getTemporaryDirectory();
            final targetPath =
          dir.absolute.path + "/lookaptPostImage$now.microsecond.jpg";

            File imgFile =
                await testCompressAndGetFile(File(tempFile.path), targetPath); 

            print("Compressed image");
            print(imgFile.lengthSync());
            fileImageArray.add(imgFile); //with image compress
          
          if (fileImageArray.length == images.length) 
            var res = await ApiService.uploadMultipleImage(files: fileImageArray);
            print("image upload response");
            print(res);
            var resp = json.decode(res);
            if (resp['status'] == true) 
              SuccessToastWidget(context, message: resp['message']);
             else 
              FailedToastWidget(context, message: resp['message']);
            
          
        );
        print("Test Prints");
        print(fileImageArray.length);
    
        return fileImageArray;
      
    
      final RoundedLoadingButtonController _loginBtnController =
          new RoundedLoadingButtonController();
    
      @override
      Widget build(BuildContext context) 
        return Scaffold(
          appBar: CommonAppBarWidget(title: widget.parameters['name'] ?? ""),
          body: _body(),
        );
      
    
      AddNEwPostController _addNEwPostController = new AddNEwPostController();
    
      Widget _body() 
        return Padding(
          padding: const EdgeInsets.only(left: 20, right: 20, top: 10),
          child: ListView(
            children: [
            
              InkWell(
                onTap: loadAssets,
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(10),
                  child: Container(
                    color: Colors.grey.shade400,
                    child: ListTile(
                      leading: Icon(
                        Icons.add_box_outlined,
                        size: 30,
                        color: Colors.black,
                      ),
                      trailing: MyTextWidgets.textWidgetSemiBold(
                          str: "Pick Images", fontSize: 20),
                    ),
                  ),
                ),
              ),
              RoundedLoadingButton(
                child: MyTextWidgets.textWidgetBold(
                    fontSize: 16, str: "Next", color: MyColors.white.redC),
                controller: _loginBtnController,
                onPressed: () 
                  _getImagePaths();                 
                ,
                width: MediaQuery.of(context).size.width,
                borderRadius: 10,
                color: MyColors.appGreenColor.redC,
                height: 44,
              ),
              Center(
                  child: _error == ""
                      ? Container()`enter code here`
                      : MyTextWidgets.textWidgetLight(str: _error)),
   
              Container(
                child: buildGridView(),
                height: 100,
                width: MediaQuery.of(context).size.width - 100,
              ),
             
            ],
          ),
        );
      



注意:

我的 ui 代码可能无法在您的代码中运行,因此请仅从屏幕代码中复制所需的代码。

Http 请求代码可以正常工作,只需复制并粘贴它即可

感谢您的支持!

【讨论】:

如果您的图像被覆盖,请使用以下代码添加文件路径。最终目标路径 = dir.absolute.path + "/lookaptPostImage$now.microsecond.jpg";在这里我添加一个带有文件路径的微秒然后问题将解决 声明 now 变量,例如 DateTime now = DateTime.now();上面添加行之前【参考方案5】:

我发布了这个带有 dio 和 image_picker 依赖的解决方案。它肯定会奏效。我为这个解决方案花了 2 天时间。

FormData formData = new FormData.fromMap(
  "name": "Max",
  "location": "Paris",
  "age": 21,
  "image[]": [
    await MultipartFile.fromFile(
      _imageFile.path,
    ),
    await MultipartFile.fromFile(
      _imageFile.path,
    ),
  ],
);

print(FormData1().then((value) 
  print(value);
));
response = await dio.post(
 
  "http://143.110.244.110/radius/frontuser/eventsubmitbutton",
  data: formData,
  onSendProgress: (received, total) 
    if (total != -1) 
      print((received / total * 100).toStringAsFixed(0) + '%');
    
  ,
);
print(response);

【讨论】:

以上是关于如何使用 HTTP 将多张图片上传到 Flutter 中的 Rest API?的主要内容,如果未能解决你的问题,请参考以下文章

如何将多张图片上传到 Cloudinary?

如何将多张图片上传到 sanity.io?

如何在 django 中将多张图片上传到博客文章

使用 AFNetworking 快速上传多张图片

Android Retrofit 实现(图文上传)文字(参数)和多张图片一起上传

在这种情况下如何将多张图片上传到远程服务器?循环或重复上传过程?