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

使用URLSession通过S3预签名URL PUT上传图片返回200但损坏

问题原因

S3预签名URL的PUT上传接口,要求请求体直接放待上传文件的原始二进制数据,不需要使用multipart/form-data格式做表单包装。
你当前代码错误地给原始图片数据拼接了multipart边界、Content-Disposition表单项头等额外协议内容,导致S3最终存储的文件开头混入了非图片的文本内容,文件格式损坏,所以只能显示为小方块异常图。
Postman测试正常是因为你在Postman中选择了二进制(binary)上传模式,直接传递了原始图片数据,没有附加multipart包装内容。你之前只复刻了请求头,没有对齐请求体格式,所以问题没有解决。

修复代码

移除所有构造formData的逻辑,上传时直接传入原始图片Data即可,修正后的实现:

func uploadImage(urlString: String, fileName: String, data: Data, onSuccess: @escaping VoidResult) {
    let session = URLSession.shared
    guard let targetUrl = URL(string: urlString) else { return }

    var urlRequest = URLRequest(url: targetUrl)
    urlRequest.httpMethod = "PUT"
    // 仅需保证Content-Type和生成预签名URL时使用的类型完全一致即可
    urlRequest.setValue("image/jpeg", forHTTPHeaderField: "Content-Type")

    // 直接上传原始图片数据,不要附加任何multipart表单内容
    session.uploadTask(with: urlRequest, from: data, completionHandler: { _, response, error in
        guard error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
            // 可按需补充错误处理逻辑
            return
        }
        DispatchQueue.main.async {
            onSuccess()
        }
    }).resume()
}
注意事项
  • 不需要手动添加Accept、Accept-Encoding、Connection、User-Agent这类请求头,URLSession会自动填充符合规范的默认值,硬编码Postman的相关头反而可能触发签名校验不通过的问题
  • 请求头中的Content-Type必须和生成预签名URL时传入的Content-Type完全一致,否则会返回签名错误
  • 只有POST形式的S3表单上传才需要使用multipart格式包装请求体,普通PUT预签名上传不需要该逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:09:30