如何在 Flutter 中使用 Dio 和 multi_image_picker 插件上传多张图片
Posted
技术标签:
【中文标题】如何在 Flutter 中使用 Dio 和 multi_image_picker 插件上传多张图片【英文标题】:How to upload multiple images using Dio and multi_image_picker plugin in Flutter 【发布时间】:2020-05-03 22:19:01 【问题描述】:我想在 Flutter 中使用 Dio 和 multi_image_picker 插件上传多张图片。
List<Asset>
这是问题所在,因为我无法从 List<Asset>
转换为 List<File>
,所以如果您有任何解决方案,请帮助我。
尝试使用:
multi_image_picker: ^4.6.1
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,),),
),
),
),
),
],
),
);
【问题讨论】:
欢迎来到 ***!这看起来是一个合理的问题 - 但如果您发布您尝试过的代码,它可能会有所帮助。 你好亲爱的,现在我准备更新我的源代码......请帮助我!谢谢! 您可以使用file_picker 并直接获取选择的文件列表。它支持多选并提供文件路径作为响应。这将是最简单的方法。 【参考方案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】:以下是如何从Asset
s 列表中获取字节数组列表:
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以上是关于如何在 Flutter 中使用 Dio 和 multi_image_picker 插件上传多张图片的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 dio 从本地主机:3000 切换到准备在 Flutter 中生产的东西?
如何使用 Dio 或 http 在 Flutter 中通过 GET 请求发送参数