通过 Flutter App 的 PreSigned URL 将文件上传到 S3。但是当我下载它时文件已损坏
Posted
技术标签:
【中文标题】通过 Flutter App 的 PreSigned URL 将文件上传到 S3。但是当我下载它时文件已损坏【英文标题】:Uploaded file to S3 via PreSigned URL from Flutter App. but the file is corrupted when i download it 【发布时间】:2019-10-27 21:18:47 【问题描述】:我正在开发一个 Flutter 应用程序,我在其中使用预签名 URL 将图像文件(PUT 请求)上传到 AWS S3。上传成功,因为我可以在 S3 中看到该文件。但是当我点击并从存储桶中下载它时,下载的文件已损坏。
我正在使用 Dio 库来上传文件。 通过邮递员将图像文件上传为二进制文件效果很好
uploadFileToPresignedS3(
File payload, String fileName, String presignedURL) async
try
Dio dio = new Dio();
FormData formData = new FormData.from(
"name": fileName, "file1": new UploadFileInfo(payload, fileName));
dio.put(presignedURL, data: formData);
catch (ex)
print(ex);
预期:上传的文件不会损坏
实际结果:上传的文件已损坏
【问题讨论】:
更新:我尝试使用 javascript XHR 上传,效果很好。 我没有使用 Dio 库,而是使用了 http.put(presignedURL, body: payload.readAsBytesSync());效果很好。 我也遇到了这个问题,可以分享一下代码吗? http.put() 工作正常...但是如何使用 put() 获取上传进度? Dio 提供了一个“onSendProgress”方法来执行此操作。 【参考方案1】:使用此代码使用 Dio 将文件(图像)上传到 S3 预签名 URL 并显示 上传进度:
await dio.put(
url,
data: image.openRead(),
options: Options(
contentType: "image/jpeg",
headers:
"Content-Length": image.lengthSync(),
,
),
onSendProgress: (int sentBytes, int totalBytes)
double progressPercent = sentBytes / totalBytes * 100;
print("$progressPercent %");
,
);
注意:不要像这样将 Content-Type 标头与 Content-Length 一起设置:
headers:
"Content-Length": image.lengthSync(),
"Content-Type": "image/jpeg",
,
由于某些原因,会导致上传文件损坏。
以防万一:您可以使用 setState()
代替 print("$progressPercent %")
在 UI 中显示更新。
希望这会有所帮助。
【讨论】:
我尝试了上述解决方案,但没有奏效。但是经过一些跟踪和错误后,我发现令人惊讶地发送“授权”标头是我的错误。如果您要发送它,请尝试将其删除。 它可以工作,但是当我上传图片时需要图片网址作为响应。 在浪费了 3 天之后,我终于想出了您的解决方案。这是在遇到情况下唯一有效的答案。谢谢【参考方案2】:要背负 Rabi Roshans 的评论,您需要将 contenType 修改为“application/octet-stream”。在后端的 S3 参数中,您需要执行相同的操作。
客户端代码
await dio.put(
url,
data: image.openRead(),
options: Options(
contentType: "application/octet-stream",
headers:
"Content-Length": image.lengthSync(),
,
),
onSendProgress: (int sentBytes, int totalBytes)
double progressPercent = sentBytes / totalBytes * 100;
print("$progressPercent %");
,
);
s3 后端
var presignedUrl = s3.getSignedUrl("putObject",
Bucket: "your_bucke_name",
Key: "filename.ext",
Expires: 120, // expirations in seconds
ContentType: "application/octet-stream", // this must be added or you will get 403 error
)
;
【讨论】:
【参考方案3】:我创建了这个类来使用预签名的 url 将图像发送到 s3,我正在使用相机库将照片发送到 s3。
import 'dart:convert';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:http/http.dart';
import 'package:http_parser/http_parser.dart';
class AwsApi
Future<String> uploadToSignedUrl(required XFile file, required String signedUrl) async
Uri uri = Uri.parse(signedUrl);
var response = await put(uri, body: await file.readAsBytes(), headers: "Content-Type": "image/jpg");
return response;
【讨论】:
以上是关于通过 Flutter App 的 PreSigned URL 将文件上传到 S3。但是当我下载它时文件已损坏的主要内容,如果未能解决你的问题,请参考以下文章
通过 Flutter App 和 JSON Web Token 在 Django 中验证用户
云队友丨招app移动开发,flutter开发工程师 5000元
Flutter 项目实战 编辑框(TextField) 自定义 七