You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter使用S3预签名URL下载文件时遇AWS 404错误求助

问题分析与解决方案

核心问题

预签名URL本身有效(硬编码可正常下载),但通过Lambda传递到Flutter后使用时,AWS返回404错误,本质是URL中的签名部分被意外修改/转义,导致签名验证失败。

排查与修复步骤

1. 对比URL原始值与解析后的值

先确认从Lambda获取的URL在解析前后是否一致,避免JSON解析或传输过程中出现字符转义:

http.Response res1 = await http.get(url2);
// 打印原始响应体,直接查看Lambda返回的URL字符串
print("Lambda原始响应体: ${res1.body}");
final data1 = cnv.jsonDecode(res1.body);
String theUrl = data1['theUrl'];
// 打印解析后的URL,和浏览器直接获取的URL对比
print("Flutter解析后URL: $theUrl");
print("浏览器获取的正确URL: [这里填浏览器拿到的URL]");

如果发现解析后的URL出现字符变化(比如+变成空格、%2F被还原为/),说明是JSON解析或传输时的编码问题。

2. 检查Hive存储对URL的影响

Hive存储字符串时可能存在隐式转义,需验证存储前后URL是否一致:

// 写入时
String rawUrl = data1['theUrl'];
await box.put('presigned_url', rawUrl);
print("写入Hive的URL: $rawUrl");

// 读取时
String storedUrl = box.get('presigned_url') ?? '';
print("从Hive读取的URL: $storedUrl");

若存储后URL发生变化,需直接存储原始字符串,避免Hive的自动处理。

3. 修正Dio请求的URL处理逻辑

Dio默认会对URL进行自动编码,若预签名URL已完成编码,重复编码会破坏签名。需直接传入Uri对象避免重复编码:

Future<File?> downloadFile(String url, String name) async {
  final appStorage = await getApplicationDocumentsDirectory();
  final file = File('${appStorage.path}/$name');

  // 用Uri.parse包装URL,避免Dio重复编码
  final response = await Dio().get(
    Uri.parse(url),
    options: Options(
      responseType: ResponseType.bytes,
      followRedirects: false,
      receiveTimeout: 0,
    ),
  );

  final raf = file.openSync(mode: FileMode.write);
  raf.writeFromSync(response.data);
  print('file saved');
  await raf.close();

  return file;
}

4. 确保Lambda返回的URL未被转义

Lambda返回JSON时,需保证theUrl字段是原始预签名URL字符串,未被JSON序列化自动转义。以Node.js Lambda为例:

exports.handler = async (event) => {
  const presignedUrl = await generateYourPresignedUrl();
  return {
    statusCode: 200,
    body: JSON.stringify({
      theStatus: "success",
      theUrl: presignedUrl // 直接传入原始URL,不要手动转义
    }),
    headers: {
      'Content-Type': 'application/json'
    }
  };
};

5. XML解析问题处理

若Lambda返回XML格式,出现“无根节点”错误说明返回的XML结构不合法。需确保Lambda返回标准XML,比如:

<response>
  <theStatus>success</theStatus>
  <theUrl>https://your-s3-presigned-url</theUrl>
</response>

使用xml包解析时,正确读取节点:

import 'package:xml/xml.dart' as xml;

final document = xml.XmlDocument.parse(res1.body);
final theUrl = document.findElements('theUrl').first.text;

内容的提问来源于stack exchange,提问作者BeeVibrant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:32:20