Flutter Web生成的PDF上传S3后损坏,如何实现正确上传?
Flutter Web 生成PDF后上传S3损坏解决方案
问题根因
你之前的代码出现PDF损坏的核心原因是:Dio在Web环境下默认会对请求数据做序列化/UTF-8编码处理,直接传入Uint8List会被当成普通文本转码,导致二进制PDF内容被篡改。同时手动设置的Content-Length如果和实际数据长度不匹配,或者和预签名URL的签名规则冲突,也会导致S3接收的文件异常。
步骤1:验证生成的PDF本身有效性
先排除PDF生成环节的问题,用以下代码将生成的Uint8List在Web端直接下载到本地测试:
import 'dart:html' as html; void verifyPdfValidity(Uint8List pdfBytes) { final pdfBlob = html.Blob([pdfBytes], 'application/pdf'); final downloadUrl = html.Url.createObjectUrlFromBlob(pdfBlob); final anchor = html.AnchorElement(href: downloadUrl) ..download = 'test_output.pdf' ..target = '_blank'; html.document.body?.append(anchor); anchor.click(); anchor.remove(); html.Url.revokeObjectUrl(downloadUrl); }
如果下载的PDF可以正常打开,说明生成环节无问题,问题出在上传流程。
步骤2:修正上传代码
改为以二进制流的形式发送数据,同时禁用Dio的默认请求转换逻辑:
import 'package:dio/dio.dart'; Future<void> uploadPdfToS3(String s3PresignedPutUrl, Uint8List pdfBytes) async { final dio = Dio(); await dio.put( s3PresignedPutUrl, // 转为Stream发送,避免Dio自动序列化二进制数据 data: Stream.fromIterable(pdfBytes.map((byte) => [byte])), options: Options( contentType: 'application/pdf', // 直接用PDF数据的实际长度,不要手动传入其他值 contentLength: pdfBytes.length, // 禁用默认请求编码器,避免二进制内容被转码 requestEncoder: (List<int> data, RequestOptions options) => data, ), onSendProgress: (sentBytes, totalBytes) { if (totalBytes != -1) { final progress = sentBytes / totalBytes * 100; print('上传进度:${progress.toStringAsFixed(1)}%'); } }, ); }
注意事项
- 生成S3预签名PUT URL时,必须指定
ContentType为application/pdf,和上传时的contentType完全一致,否则会触发S3签名校验失败,导致文件损坏 - 不要额外添加未在预签名URL计算时包含的请求头,避免签名不匹配
- 不需要引入任何文件读写相关的库,直接操作
Uint8List即可完成Web端的上传流程
内容的提问来源于stack exchange,提问作者RIP71DE
相关产品推荐
相关产品推荐

