如何解决Flutter Web 多部分表单数据文件上传进度条
我使用 Flutter web 和 Strapi headless cms 作为后端。我能够成功发送文件,但想要它的进度指示。后端限制:文件上传必须是多部分表单数据,无论是缓冲区还是流。前端限制:Flutter web 无权访问系统文件目录;文件必须加载到内存中并使用其字节发送。
我可以使用 flutter 的 http 包或 Dio 包上传文件,但是在尝试以某种方式访问上传进度时遇到以下问题:
Http 示例代码:
http.StreamedResponse response;
final uri = Uri.parse(url);
final request = MultipartRequest(
'POST',uri,);
request.headers['authorization'] = 'Bearer $_token';
request.files.add(http.MultipartFile.fromBytes(
'files',_fileToUpload.bytes,filename: _fileToUpload.name,));
response = await request.send();
var resStream = await response.stream.bytesToString();
var resData = json.decode(resStream);
我的尝试: 当访问 onData 的 response.stream 时,它仅在服务器发送完成的请求时响应(即使方法声明它应该获得一些进度指示)。
Dio 封装代码
Response response = await dio.post(url,data: formData,options: Options(
headers: {
'authorization': 'Bearer $_token',},),onSendProgress: (int sent,int total) {
setState(() {
pm.progress = (sent / total) * 100;
});
问题:
似乎该包能够获得一些进度指示,但是用于 Flutter web 的 Dio 包有一个尚未修复的错误:请求会阻止 ui,并且应用程序在上传完成之前会冻结。
解决方法
您好,可以使用universal_html/html.dart包来做进度条,步骤如下:
- 导入通用包
import 'package:universal_html/html.dart' as html;
- 从 html 输入元素中选择文件,而不是使用文件选择器包
_selectFile() {
html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = false;
uploadInput.accept = '.png,.jpg,.glb';
uploadInput.click();
uploadInput.onChange.listen((e) {
_file = uploadInput.files.first;
});
}
- 将upload_worker.js创建到web文件夹中,我的例子是上传到S3 post presigned url
self.addEventListener('message',async (event) => {
var file = event.data.file;
var url = event.data.url;
var postData = event.data.postData;
uploadFile(file,url,postData);
});
function uploadFile(file,presignedPostData) {
var xhr = new XMLHttpRequest();
var formData = new FormData();
Object.keys(presignedPostData).forEach((key) => {
formData.append(key,presignedPostData[key]);
});
formData.append('Content-Type',file.type);
// var uploadPercent;
formData.append('file',file);
xhr.upload.addEventListener("progress",function (e) {
if (e.lengthComputable) {
console.log(e.loaded + "/" + e.total);
// pass progress bar status to flutter widget
postMessage(e.loaded/e.total);
}
});
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
// postMessage("done");
}
}
xhr.onerror = function () {
console.log('Request failed');
// only triggers if the request couldn't be made at all
// postMessage("Request failed");
};
xhr.open('POST',true);
xhr.send(formData);
}
- Flutter web call upload worker 上传和监听进度条状态
class Upload extends StatefulWidget {
@override
_UploadState createState() => _UploadState();
}
class _UploadState extends State<Upload> {
html.Worker myWorker;
html.File file;
_uploadFile() async {
String _uri = "/upload";
final postData = {};
myWorker.postMessage({"file": file,"uri": _uri,postData: postData});
}
_selectFile() {
html.InputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = false;
uploadInput.click();
uploadInput.onChange.listen((e) {
file = uploadInput.files.first;
});
}
@override
void initState() {
myWorker = new html.Worker('upload_worker.js');
myWorker.onMessage.listen((e) {
setState(() {
//progressbar,...
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
onPressed: _selectFile,child: Text("Select File"),),RaisedButton(
onPressed: _uploadFile,child: Text("Upload"),],);
}
}
就是这样,希望能帮到你。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。