【问题标题】:How to upload images and file to a server in Flutter?如何在 Flutter 中将图像和文件上传到服务器?
【发布时间】:2018-08-13 23:28:02
【问题描述】:

我使用网络服务进行图像处理,它在 Postman 中运行良好:

现在我想用 Dart 发出 http 请求:

import 'package:http/http.dart' as http;

static ocr(File image) async {
    var url = '${API_URL}ocr';
    var bytes = image.readAsBytesSync();

    var response = await http.post(
        url,
        headers:{ "Content-Type":"multipart/form-data" } ,
        body: { "lang":"fas" , "image":bytes},
        encoding: Encoding.getByName("utf-8")
    );

    return response.body;

  }

但我不知道如何上传图像文件,在上面的代码中我得到异常:Bad state: Cannot set the body fields of a Request with content-type "multipart/form-data".
请求正文应该怎么写?

【问题讨论】:

  • 解决方法:我要求我的服务器人员更改服务器 api 以接受 base64 编码图像。所以我把base64编码的图像作为字符串放在正文中,标题的内容类型等于application/x-www-form-urlencoded,它可以工作。
  • 这里回答了类似的问题stackoverflow.com/questions/44841729/…
  • @AravindVemula 我不想发送 base64 编码字节
  • 这个答案帮助了我stackoverflow.com/a/49645074/6133481
  • 您是否尝试过使用内容类型“application/octet-stream”。我总是尽可能避免“多部分/表单数据”。设计最好的文件上传 API 在 POST 正文中接受“application/octet-stream”,并且任何参数都在 URI 中。

标签: flutter dart file-upload httprequest


【解决方案1】:

您的解决方法应该有效;许多服务器将接受 application/x-www-form-urlencoded 作为替代方案(尽管数据的编码效率较低)。

但是,可以使用 dart:http 来执行此操作。与其使用http.post,不如使用http.MultipartFile 对象。

来自dart documentation

var request = new http.MultipartRequest("POST", url);
request.fields['user'] = 'someone@somewhere.com';
request.files.add(http.MultipartFile.fromPath(
    'package',
    'build/package.tar.gz',
    contentType: new MediaType('application', 'x-tar'),
));
request.send().then((response) {
  if (response.statusCode == 200) print("Uploaded!");
});

【讨论】:

  • 文档有误,this github issues
  • 谢谢@Kiana,我没注意到。现在已经修好了。虽然 dart.http 的 master 与当前发布的 0.11.3+16 有很大不同,但我预计这最终会变得不正确。
  • 谢谢兄弟,你的代码帮助我解决了 Flutter 中 MultipartFile 中的发送字段(字符串)
  • @rmtmckenzie MultipartFile.fromPath 中的“package”和“build/package.tar.gz”参数是什么
  • Package 是字段的名称(如果它是 Web 上的表单,它将是输入的名称),而 build/package.tar.gz 是路径。不过,该示例确实更具体到服务器;您可以使用 MultipartFile 的其他构造函数之一,例如 .fromBytes 或使用流的构造函数。
【解决方案2】:

向你推荐dio包,dio是Dart/Flutter强大的Http客户端,支持拦截器、FormData、请求取消、文件下载、超时等。

dio 非常好用,在这种情况下你可以:

发送表单数据:

FormData formData = new FormData.from({
   "name": "wendux",
   "file1": new UploadFileInfo(new File("./upload.jpg"), "upload1.jpg")
});
response = await dio.post("/info", data: formData)

更多详情请参考dio

【讨论】:

  • 请在此处写下解决方案,而不是包含将来可能会损坏的链接。谢谢!
  • 可以用DIO修改图片文件名吗?
  • @wendu 我能知道 UploadFileInfo() 函数来自哪里吗?
  • @dipgirl UploadFileInfo 已被弃用,现在有 MultiPartFromFile 类可以做到这一点。这是一个示例github.com/flutterchina/dio#sending-formdata
【解决方案3】:

这可以使用 MultipartRequest 类 (https://pub.dev/documentation/http/latest/http/MultipartRequest-class.html) 来实现

根据需要更改媒体类型和 uri。

uploadFile() async {
    var postUri = Uri.parse("<APIUrl>");
    var request = new http.MultipartRequest("POST", postUri);
    request.fields['user'] = 'blah';
    request.files.add(new http.MultipartFile.fromBytes('file', await File.fromUri("<path/to/file>").readAsBytes(), contentType: new MediaType('image', 'jpeg')))

    request.send().then((response) {
      if (response.statusCode == 200) print("Uploaded!");
    });
  }

【讨论】:

  • 工作就像一个魅力。 :) tnq 这么多 :) 只是要添加:- 请添加“import 'package:http_parser/http_parser.dart';” for contentType
  • 对我有用,只是将 File.fromUri("&lt;path/to/File"&gt;) 更改为 File.fromUri(Uri.parse("&lt;path/to/file&gt;"))
  • 为什么响应没有 response.body
  • @BagusAjiSantoso request.send 不返回Future&lt;Response&gt;,它返回Future&lt;StreamedResponse&gt;。看到这个问题stackoverflow.com/questions/55520829/…
  • 重要的“MediaType”来自哪里?
【解决方案4】:

我找到了一个没有使用任何外部插件的工作示例,这只使用了

import 'package:http/http.dart' as http;
import 'dart:io';
import 'package:path/path.dart';
import 'package:async/async.dart';
import 'dart:convert';

代码

var stream =
        new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
    // get file length
    var length = await imageFile.length(); //imageFile is your image file
    Map<String, String> headers = {
      "Accept": "application/json",
      "Authorization": "Bearer " + token
    }; // ignore this headers if there is no authentication

    // string to uri
    var uri = Uri.parse(Constants.BASE_URL + "api endpoint here");

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

  // multipart that takes file
    var multipartFileSign = new http.MultipartFile('profile_pic', stream, length,
        filename: basename(imageFile.path));

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

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

    //adding params
    request.fields['loginId'] = '12';
    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);

    });

【讨论】:

  • basename是什么意思
  • 导入这个包 import 'package:path/path.dart';
【解决方案5】:

如何在flutter/dart中使用restAPI上传图片文件。

这对我有用。

var postUri = Uri.parse("apiUrl");

http.MultipartRequest request = new http.MultipartRequest("POST", postUri);

http.MultipartFile multipartFile = await http.MultipartFile.fromPath(
    'file', filePath); 

request.files.add(multipartFile);

http.StreamedResponse response = await request.send();


print(response.statusCode);

【讨论】:

    【解决方案6】:

    使用MultipartRequest 类。 如何在flutter/dart中使用restAPI上传图片文件

      void uploadImage1(File _image) async {
    
        // open a byteStream
        var stream = new http.ByteStream(DelegatingStream.typed(_image.openRead()));
        // get file length
        var length = await _image.length();
    
        // string to uri
        var uri = Uri.parse("enter here upload URL");
    
        // create multipart request
        var request = new http.MultipartRequest("POST", uri);
    
        // if you need more parameters to parse, add those like this. i added "user_id". here this "user_id" is a key of the API request
        request.fields["user_id"] = "text";
    
        // multipart that takes file.. here this "image_file" is a key of the API request
        var multipartFile = new http.MultipartFile('image_file', stream, length, filename: basename(_image.path));
    
        // add file to multipart
        request.files.add(multipartFile);
    
        // send request to upload image
        await request.send().then((response) async {
          // listen for response
          response.stream.transform(utf8.decoder).listen((value) {
            print(value);
          });
    
        }).catchError((e) {
          print(e);
        });
      }
    

    命名空间:

    import 'package:path/path.dart';
    import 'package:async/async.dart';
    import 'dart:io';
    import 'package:http/http.dart' as http;
    

    【讨论】:

      【解决方案7】:

      使用表单数据将图像上传到服务器

      要将图像上传到服务器,您需要一个 dio 库。

      特点:

      1. 授权(添加令牌)
      2. 添加额外字段,例如:用户名等
      3. 添加要上传的图片

      代码示例:

      import 'package:dio/dio.dart' as dio;
      import 'dart:convert';
      
          try {
            ///[1] CREATING INSTANCE
            var dioRequest = dio.Dio();
            dioRequest.options.baseUrl = '<YOUR-URL>';
      
            //[2] ADDING TOKEN
            dioRequest.options.headers = {
              'Authorization': '<IF-YOU-NEED-ADD-TOKEN-HERE>',
              'Content-Type': 'application/x-www-form-urlencoded'
            };
      
            //[3] ADDING EXTRA INFO
            var formData =
                new dio.FormData.fromMap({'<SOME-EXTRA-FIELD>': 'username-forexample'});
      
            //[4] ADD IMAGE TO UPLOAD
            var file = await dio.MultipartFile.fromFile(image.path,
                  filename: basename(image.path),
                  contentType: MediaType("image", basename(image.path)));
      
            formData.files.add(MapEntry('photo', file));
      
            //[5] SEND TO SERVER
            var response = await dioRequest.post(
              url,
              data: formData,
            );
            final result = json.decode(response.toString())['result'];
          } catch (err) {
            print('ERROR  $err');
          }
      

      【讨论】:

      • “MediaType”出现错误。我错过了任何进口吗?
      • 导入'包:http_parser/http_parser.dart';这应该可以解决您的问题。
      【解决方案8】:

      要添加标头并使用带有https://pub.dev/packages/multi_image_picker 插件的http multipart,

      这是代码。

      var request =  http.MultipartRequest(
              'POST', Uri.parse(myurl)
      
            );
            //Header....
            request.headers['Authorization'] ='bearer $authorizationToken';
            
             request.fields['PropertyName'] = propertyName;
          request.fields['Country'] = country.toString();
          request.fields['Description'] = des;
          request.fields['State'] = state.toString();
             request.files.add(http.MultipartFile.fromBytes(
            'ImagePaths',
            learnImage,
            filename: 'some-file-name.jpg',
        contentType: MediaType("image", "jpg"),
          )
              );
      var response = await request.send();
      print(response.stream);
      print(response.statusCode);
      final res = await http.Response.fromStream(response);
        print(res.body);
      

      使用 HTTP 和https://pub.dev/packages/image_picker PLUGIN

      这是代码

      var request =  http.MultipartRequest(
              'POST', Uri.parse(myurl)
      
            );
            request.headers['Authorization'] ='bearer $authorizationToken';
             request.fields['PropertyName'] = propertyName;
          request.fields['Country'] = country.toString();
          request.fields['Description'] = des;
          request.fields['State'] = state.toString();
             request.files.add(await http.MultipartFile.fromPath(
            'ImagePaths',
            file.path
          )
              );
      var response = await request.send();
          print(response.stream);
          print(response.statusCode);
          final res = await http.Response.fromStream(response);
            print(res.body);
      

      【讨论】:

      • 为了提高未来的回答质量,请考虑为某些上下文添加一些文本以及您对所提供代码的看法。
      • 我试过了,但没有工作文件没有在服务器最终请求中接收到 = http.MultipartRequest('POST', Uri.parse('yurr.app/api/register-celebrity')); request.fields['title'] = title.text; request.fields['sub_title'] = subTitle.text; request.files .add(await http.MultipartFile.fromPath('profile_photo', photo.path)); request.files .add(await http.MultipartFile.fromPath('profile_video', video.path)); var response = 等待 request.send(); var responseString = 等待 response.stream.bytesToString();打印(响应字符串);
      • 你用的是哪个插件?
      【解决方案9】:

      2021 年更新方式:

      使用颤振http mime

      import 'package:mime/mime.dart';
      import 'package:http/http.dart' as http;
      import 'package:http_parser/http_parser.dart';
      import 'dart:io';
      
      
        Future<dynamic> multipartImageUpload(String baseUrl, String api, File image) async {
          var uri = Uri.parse(baseUrl + api);
          final mimeTypeData =
              lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
      
          // Intilize the multipart request
          final imageUploadRequest = http.MultipartRequest('PUT', uri);
      
          // Attach the file in the request
          final file = await http.MultipartFile.fromPath('image', image.path,
              contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
          imageUploadRequest.files.add(file);
      
          // add headers if needed
          //imageUploadRequest.headers.addAll(<some-headers>);
      
          try {
            final streamedResponse = await imageUploadRequest.send();
            final response = await http.Response.fromStream(streamedResponse);
            return response;
          } catch (e) {
            print(e);
            return null;
          }
        }
      

      【讨论】:

      • 如何PUT数据?
      • PUT 数据是什么意思?如果您仅通过字段表示数据,则 imageUploadRequest.fields['fieldName'] = 'your value';
      【解决方案10】:

      如果有人尝试使用 MultipartRequest 方法上传 pdf 或任何其他文档,请将其留在这里。

      只需将内容类型添加为 - contentType: new MediaType('application', 'pdf')

      【讨论】:

        【解决方案11】:

        工作代码

        String path = userSelectedImagePath;
            Map<String, String> data = {
              "name": firstName!,
              "email": userEmail!
            };
           
        
            String token = await LocaldbHelper().getToken();
            Map<String, String> headers = {
              'X-Requested-With': 'XMLHttpRequest',
              'authorization': 'Bearer $token',
            };
        
        
           var request = http.MultipartRequest(
                'POST',
                Uri.parse(ApiUrl.updateProfile),
              );
              request.fields.addAll(data);
              request.headers.addAll(headers);
              var multipartFile = await http.MultipartFile.fromPath(
                  'avatar', path); //returns a Future<MultipartFile>
              request.files.add(multipartFile);
              http.StreamedResponse response = await request.send();
              final respStr = await response.stream.bytesToString();
              var jsonData = jsonDecode(respStr);
              if (response.statusCode == 200) {
                // success
              } else {
                // error
              }
        

        【讨论】:

        • 使用支持信息改进您的答案,例如解释代码以便更好地理解。
        【解决方案12】:

        与听者 上传图片

        Future uploadImageMedia(File fileImage, String token) async {
        
        
          final mimeTypeData =
                lookupMimeType(fileImage.path, headerBytes: [0xFF, 0xD8]).split('/');
                 final imageUploadRequest =
                http.MultipartRequest('POST', Uri.parse(mainUrlSite + "wp-json/wp/v2/media"));
        
            final file = await http.MultipartFile.fromPath('file', fileImage.path,
                contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
        
            imageUploadRequest.files.add(file);
            imageUploadRequest.headers.addAll({
              "Authorization": "Bearer " + token
            });
            try {
              final streamedResponse = await imageUploadRequest.send();
        
              streamedResponse.stream.transform(utf8.decoder).listen((value) {
                print(value);
                return Future.value(value);
              });
            } catch (e) {
              print(e);
            }
        }
        

        【讨论】:

        • 能不能添加这个方法定义lookupMimeType()..
        【解决方案13】:

        我使用 Dio 库和 put 方法:

            var formData = FormData.fromMap({
              'simpleParam': 'example',
              'file': await MultipartFile.fromFile(filePath, filename: 'file.jpg')
            });
        
            var dio = Dio();
            dio.options.headers[HttpHeaders.authorizationHeader] = myToken;
        
            var response = new Response(); //Response from Dio
            response = await dio.put(myUrl + "/myApi", data: formData);
        

        结果在 response.data 中

        【讨论】:

          【解决方案14】:

          Dio 和 FilePicker 的良好代码可用于在您的服务器上发布文件。我在 web 上使用 Flutter。

          FilePicker Dio

          1. 首先你需要编写 Dio post 方法。
              Future postImportClient(PlatformFile file) async {
                  try {
                    var urlBase = 'your url';
                    var mfile = MultipartFile.fromBytes(file.bytes!, filename: file.name);
                    var formData = FormData();
                    formData.files.add(MapEntry('file', mfile));
                    await _dio.post(urlBase, data: formData);
                  } on DioError catch (error) {
                    throw Exception(error);
                  }
                }
          
          1. 初始 FilePicker 并获取文件。
          FilePickerResult? result = await FilePickerWeb.platform.pickFiles();
          if (result != null) {
          var file = result.files.single;
          await client.postImportClient(file);
          }
          

          祝你好运!

          【讨论】:

          • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
          【解决方案15】:

          使用 dio 我喜欢这样:

          Future<void> _uploadFileAsFormData(String path) async {
            try {
              final dio = Dio();
          
              dio.options.headers = {
                'Content-Type': 'application/x-www-form-urlencoded'
              };
          
              final file =
                await MultipartFile.fromFile(path, filename: 'test_file');
          
              final formData = FormData.fromMap({'file': file}); // 'file' - this is an api key, can be different
          
              final response = await dio.put( // or dio.post
                uploadFileUrlAsString,
                data: formData,
              );
            } catch (err) {
              print('uploading error: $err');
            }
          }
          

          【讨论】:

            【解决方案16】:

            MultipartFile 类中有一个静态方法,称为 fromPath,它会返回 Future。您可以使用request.files.add() 方法将文件添加到您的请求正文中。

            final postUri = Uri.parse(kAPIUrl);
            http.MultipartRequest request = http.MultipartRequest('POST', postUri);
            
            http.MultipartFile multipartFile =
            await http.MultipartFile.fromPath('image_file', filePath); //returns a Future<MultipartFile>
            
            request.files.add(multipartFile);
            
            http.StreamedResponse response = await request.send();
            
            

            【讨论】:

              猜你喜欢
              • 2017-12-04
              • 2021-01-18
              • 1970-01-01
              • 2020-01-01
              • 2011-08-12
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多