app端的上传 获取到FilePickerResult 对象 里面包含 选择上传的文件信息
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: true,
);
app端上传逻辑
final allLength = file?.count ?? 0;
int curUploadLength = 0;
// 文件为空
for (var path in file!.paths) {
final res =
await Api.uploadFile(path as String);
final String httpPath = res["data"] ?? "";
imgList.add(httpPath);
curUploadLength++;
widget.onChange?.call(imgList);
}
setState(() {
cruProgress = curUploadLength / allLength;
});
setState(() {
isUploading = false;
});
api上传
static uploadFile(
String path, {
ProgressCallback? onSendProgress,
}) async {
FormData formData =
FormData.fromMap({'file': await MultipartFile.fromFile(path)});
final res = await dio.post(
Url.uploadFile,
data: formData,
options: Options(
method: "post",
contentType: "multipart/form-data",
),
onSendProgress: onSendProgress,
);
return res.data;
}
其中file.count 表示上传的文件数量
file.paths 表示 选择文件的路径数组
通过dio 上传 使用FormData 进行转载
Global.isWeb == kIsWeb
web端上传报错: 不能使用file.paths 来上传文件
修改如下
web 使用 dio 和 file_picker 上传需要处理
获取 file_picker 的 PlatformFile 对象是一致的 ,在点击文件上传的时候进行逻辑处理
if (kIsWeb) {
final allLength = file?.count ?? 0;
int curUploadLength = 0;
print(file);
// 文件为空
for (PlatformFile curFile in file!.files) {
final res =
await Api.uploadFilePlatformFile(curFile);
final String httpPath = res["data"] ?? "";
imgList.add(httpPath);
curUploadLength++;
widget.onChange?.call(imgList);
}
Console.d("上传 click $allLength");
setState(() {
cruProgress = curUploadLength / allLength;
});
setState(() {
isUploading = false;
});
web端的api
static uploadFilePlatformFile(
PlatformFile file, {
ProgressCallback? onSendProgress,
}) async {
List<int> fileBytes = file.bytes!;
MultipartFile multipartFile =
MultipartFile.fromBytes(fileBytes, filename: file.name);
FormData formData = FormData.fromMap({'file': multipartFile});
final res = await dio.post(
Url.uploadFile,
data: formData,
options: Options(
method: "post",
contentType: "multipart/form-data",
),
onSendProgress: onSendProgress,
);
return res.data;
}
最关键的问题
List<int> fileBytes = file.bytes!;
file.bytes 返回的是 Uint8List 需要转成 List<int> 才行 不然 虽然文件通过FormData 发送给后端了 但是后端一直拿不到数据。
完整组件代码
// 文件上传
import 'package:LS/common/index.dart';
import 'package:LS/gen/assets.gen.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class MoreFileUploadWidget extends StatefulWidget {
final Function(List<String?>)? onChange;
const MoreFileUploadWidget({
super.key,
this.onChange,
});
@override
State<MoreFileUploadWidget> createState() => _MoreFileUploadWidgetState();
}
class _MoreFileUploadWidgetState extends State<MoreFileUploadWidget> {
FilePickerResult? file;
double cruProgress = 0;
bool isUploading = false;
List<String> imgList = [];
Future getFile() async {
setState(() {
imgList.clear();
cruProgress = 0;
isUploading = false;
});
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: true,
);
file = result;
setState(() {});
}
Widget addContainer() {
return InkWell(
child: Container(
width: 108.w,
height: 108.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0xffF4F6F8),
borderRadius: BorderRadius.circular(5.r),
),
child: Assets.icon.addImage.image(width: 34.w),
),
onTap: () async {
await getFile();
},
);
}
@override
Widget build(BuildContext context) {
return file == null
? addContainer()
: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final width = (constraints.maxWidth - 30) / 2;
return Wrap(
spacing: 30,
runSpacing: 30,
children: [
for (final name in file!.names)
Container(
width: width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
(name as String).tr,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
),
),
),
addContainer(),
if (isUploading)
Text(
"上传中...".tr,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
),
),
if (file?.names != null && file!.names.isNotEmpty)
LineProgressWidget(
cruProgress: cruProgress,
),
if (file?.names != null &&
file!.names.isNotEmpty &&
cruProgress != 1)
Center(
child: PrimaryBtn(
text: "上传",
onTap: () async {
if (isUploading) {
return;
}
Console.d("上传 click");
setState(() {
isUploading = true;
});
if (Global.isWeb) {
final allLength = file?.count ?? 0;
int curUploadLength = 0;
print(file);
// 文件为空
for (PlatformFile curFile in file!.files) {
final res =
await Api.uploadFilePlatformFile(curFile);
final String httpPath = res["data"] ?? "";
imgList.add(httpPath);
curUploadLength++;
widget.onChange?.call(imgList);
}
Console.d("上传 click $allLength");
setState(() {
cruProgress = curUploadLength / allLength;
});
setState(() {
isUploading = false;
});
} else {
try {
final allLength = file?.count ?? 0;
int curUploadLength = 0;
// 文件为空
for (var path in file!.paths) {
final res =
await Api.uploadFile(path as String);
final String httpPath = res["data"] ?? "";
imgList.add(httpPath);
curUploadLength++;
widget.onChange?.call(imgList);
}
setState(() {
cruProgress = curUploadLength / allLength;
});
setState(() {
isUploading = false;
});
} catch (e) {
Console.d("错误 $e");
setState(() {
isUploading = false;
});
}
}
},
),
),
],
);
},
);
}
}