通过 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探索及优化

云队友丨招app移动开发,flutter开发工程师 5000元

Flutter 项目实战 编辑框(TextField) 自定义 七

通过 Flutter App 的 PreSigned URL 将文件上传到 S3。但是当我下载它时文件已损坏

在android studio中自定义flutter app:打开android flutter代码