使用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
相关产品推荐
相关产品推荐

