如何使用 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_picker
link,如何修改下面的代码来上传多张图片?谢谢
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?的主要内容,如果未能解决你的问题,请参考以下文章