如何在Flutter中将图片上传到服务器?

51

我想上传一张图片,我正在使用 http.Client() 发送请求。

static uploadImage(String id, File file) {
  var httpClient = createHttpClient();

  Map<String, String> headers = new Map<String, String>();
  headers.putIfAbsent("Authorization", () => "---");
  headers.putIfAbsent("Content-Type", () => "application/json");

  var body=new List();
  body.add(id.)
  httpClient.post(URL_UPLOADIMAGE,headers: headers,body: ,encoding: )
}

请求的主体和编码部分应该是什么?


1
你应该能够使用来自dart question的相同方法! - German Saprykin
那样做可以解决问题,但是那个答案来自于旧版本的库。 - karan vs
3
request.files.add( new http.MultipartFile.fromBytes( "file", file.readAsBytesSync(), filename: "Photo.jpg", contentType: new MediaType("image", "jpg") ) );//现在已经可以正常工作了。 - karan vs
遇到同样的问题,您能分享解决方案吗? - wil
15个回答

67

使用MultipartRequest

Upload(File imageFile) async {    
    var stream = new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
      var length = await imageFile.length();

      var uri = Uri.parse(uploadURL);

     var request = new http.MultipartRequest("POST", uri);
      var multipartFile = new http.MultipartFile('file', stream, length,
          filename: basename(imageFile.path));
          //contentType: new MediaType('image', 'png'));

      request.files.add(multipartFile);
      var response = await request.send();
      print(response.statusCode);
      response.stream.transform(utf8.decoder).listen((value) {
        print(value);
      });
    }

命名空间:

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

2
我的上传文件总是application/octet-stream。有没有办法从文件中获取正确的内容类型,以及如何在请求中设置它? - alex351
2
我应该从哪里导入MediaType类? - nivla360
3
我想我只需要导入“package:http_parser/http_parser.dart”这个包。 - nivla360
4
DelegatingStream.typed 已经被弃用。 - GunJack
3
请使用 var stream = new http.ByteStream(_image.openRead()); stream.cast();,而不是 DelegatingStream.typed - Pratik Butani
显示剩余6条评论

31

最简单的方法是使用http库。

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

_asyncFileUpload(String text, File file) async{
   //create multipart request for POST or PATCH method
   var request = http.MultipartRequest("POST", Uri.parse("<url>"));
   //add text fields
   request.fields["text_field"] = text;
   //create multipart using filepath, string or bytes
   var pic = await http.MultipartFile.fromPath("file_field", file.path);
   //add multipart to request
   request.files.add(pic);
   var response = await request.send();

   //Get the response from the server
   var responseData = await response.stream.toBytes();
   var responseString = String.fromCharCodes(responseData);
   print(responseString);
}

1
你好,它给了我一个未处理的异常:SocketException: OS Error: Broken pipe, errno = 32,请建议。 - Jon Stark
请检查URL。确保您能够从Postman发布文件数据。 - Raj Yadav
我该如何在我的 .php 文件中接收它? - Santiago Arteaga
我不太擅长PHP,但是你可以使用$_FILES["file_field"]获取文件。 - Raj Yadav
@Santiago 如果我回答晚了,那么它可能会帮助其他人。在服务器端,在我的情况下,我将其视为来自HTML表单的普通文件,并且一切顺利。 - Angelwise

12

查看 submitForm() 方法中的主体。

File _image;

Future cameraImage() async {
  var image = await ImagePicker.pickImage(
    source: ImageSource.camera,
    maxHeight: 240.0,
    maxWidth: 240.0,
  );

  setState(() {
    _image = image;
  });
}

submitForm() async {
  final response = await http.post(
    uri,
    headers: {
      AuthUtils.AUTH_HEADER: _authToken
    },
    body: {
      'user_id': userId
      'photo': _image != null ? 'data:image/png;base64,' +
          base64Encode(_image.readAsBytesSync()) : '',
    },
  );

  final responseJson = json.decode(response.body);

  print(responseJson);
}

我该如何在我的 .php 文件中接收它? - Santiago Arteaga
@Santiago 你需要解码并保存。你可以在这里找到解决方案:https://dev59.com/H2gu5IYBdhLWcg3wMkQm?answertab=oldest#tab-top - Aravind Vemula
1
Base64很容易但带宽费用非常高昂...数据发送量增加了30%...不建议使用。 - emanuel sanga

10

我尝试了上述所有方法,但都无法让我将文件上传到服务器。

经过深入搜索,我找到了一个与Dio相同的插件。

以下代码可将文件上传到服务器。

uploadFileFromDio(UserProfile userProfile, File photoFile) async {
    var dio = new Dio();
    dio.options.baseUrl = url;
    dio.options.connectTimeout = 5000; //5s
    dio.options.receiveTimeout = 5000;
    dio.options.headers = <Header Json>;
    FormData formData = new FormData();
    formData.add("user_id", userProfile.userId);
    formData.add("name", userProfile.name);
    formData.add("email", userProfile.email);

    if (photoFile != null &&
        photoFile.path != null &&
        photoFile.path.isNotEmpty) {
      // Create a FormData
      String fileName = basename(photoFile.path);
      print("File Name : $fileName");
      print("File Size : ${photoFile.lengthSync()}");
      formData.add("user_picture", new UploadFileInfo(photoFile, fileName));
    }
    var response = await dio.post("user/manage_profile",
        data: formData,
        options: Options(
            method: 'POST',
            responseType: ResponseType.PLAIN // or ResponseType.JSON
            ));
    print("Response status: ${response.statusCode}");
    print("Response data: ${response.data}");
  }

"content-type" 基本上是在 API 的头文件中设置的,因此您必须添加一个标题参数作为内容类型,无论 API 开发人员设置了什么。一些常见的内容类型示例包括“text/plain”,“application/xml”,“text/html”,“application/json”,“image/gif”和“image/jpeg”。 - TejaDroid
我正在从移动设备发送文件(图像、文档等)到使用 multer 将文件存储到 MongoDB 的 Node.js API。我还有一个与同一 API 通信的 Web 应用程序。如果我通过我的移动应用程序和 Dio 插件上传图像,则服务器和我的 mobgodb 上的 mime-type 为 "application/octet-stream"。如果我通过 Web 应用程序上传它,则 mime-type 为 "image/jpeg"。我不需要在 Web 应用程序中手动设置 content-type。 - alex351
我该如何在我的.php文件中接收它? - Santiago Arteaga
我不确定,但也许您需要通过“$_FILES”检查.php中接收请求的内容,在那里您可以使用参数“user_profile”找到所有请求中的图像。 - TejaDroid
add方法来自于FormData类,它的定义在dio中。因此,请在您的pubspec.yaml中添加最新版本的dio依赖项,然后运行Packages upgrade - TejaDroid
显示剩余3条评论

7
我找到了一个可行的示例,不需要使用任何外部插件,只需使用以下内容。
import 'package:http/http.dart' as http;

代码

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

    });

1
当我使用你的代码时,它会给我一个错误,说“image”:[“没有提交文件。”]。有什么解决办法吗? - Davrick

6

请尝试以下解决方案

Future<String> uploadImageHTTP(file, url) async {

  var request = http.MultipartRequest('POST', Uri.parse(url));
  request.files.add(await http.MultipartFile.fromPath('picture', file.path));
  var res = await request.send();
  return res.reasonPhrase;

}

我遇到了这个错误。因为函数 'uploadImageHTTP' 的返回类型是 'Future<String>',所以无法返回类型为 'String?' 的值。 - Umashankar Das
考虑空安全性的更改 - Boban

5
考虑使用Flutter的Firebase Storage插件——它具有上传移动连接上的大型图像文件可能会有用的功能。
我编写了这个插件,欢迎提出贡献和反馈!

谢谢,我使用http客户端库成功完成了它。 - karan vs
1
使用我编写的 Firebase 插件不是上传图片到 Flutter 的答案。 - Jeppe

4

首先从图库或相机中选择您的图片

File _image;
Future _getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
});
}

现在,在按钮单击或_inside _getImage()函数中调用以下函数。 通过我上传的文件,您还可以看到其他字段,这些字段也包含在saveInAttendance()中。
不要忘记导入软件包:
import 'package:dio/dio.dart';
import 'package:path/path.dart';

Future saveInAttendance( BuildContext context,String entryType,String mode) async {
        Dio dio = new Dio();
        FormData formData = new FormData(); // just like JS
        formData.add("inimageFile", new UploadFileInfo(_image, basename(_image.path)));
        formData.add("compID",2);
        formData.add("company_id",2);
        formData.add("EntryType", entryType);
        formData.add("emp_code", 5);
        formData.add("Mode",mode);
        formData.add("location",""+_startLocation.latitude.toString()+"-"+_startLocation.longitude.toString());
        dio.post(url_save_attendance, data: formData, options: Options(
            method: 'POST',
            responseType: ResponseType.json // or ResponseType.JSON
        ))
            .then((r) {
          setState(() {
            var data = json.decode(r.toString());
            if(data["apiMessage"].contains('Saved')){
              warningAlert("Attendance Saved", "Your attendance saved Successfully",context);
            }
          });
        }).catchError(print);
      }

更多信息请访问这里


2

从请求中获取Body而不是

response.stream.transform(utf8.decoder).listen((value) {
    print(value);
  });

我使用:

String body=await response.stream.bytesToString()

1
updateProfile() async {
        try {
          if (_formKey.currentState.validate()) {
            _formKey.currentState.save();
            var dio = new Dio();
            var formData = FormData.fromMap({
              'name': name,
              'con_person_name': concernedPersonName,
              'email': email,
              'phone': phoneNumber,
              'password': password,
              'token': token,
              'user_type': '3',
              'license_no': licenceNumber,
              'gstno': gstNumber,
              'address': address,
              'hospital_id': '102'
              'image': await MultipartFile.fromFile(_image?.path,
            filename: _image.path.split('/').last ?? 'image.jpeg'),
           
            });
            var response = await dio.post('$SERVER_ADDRESS/api/doctorregister',
                data: formData);
            print(response.statusCode);
            print(response.data);
          }
        } catch (error) {
          print(error.toString());
        }
      }

2
请同时提供解释。 - Thavas Antonio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接