使用createPresignedPost上传MP3报400错误,求排查方案
解决S3预签名POST上传MP3返回400错误的问题
问题根源分析
Content-Type条件配置错误
预签名生成时的Conditions里错误限制了$Content-Type为multipart/form-data,但实际上传的MP3文件对应的Content-Type应为audio/mpeg,条件不匹配直接触发400错误。手动设置请求Content-Type导致格式损坏
前端上传时手动指定'Content-Type': 'multipart/form-data',但FormData会自动生成包含boundary的正确请求头,手动设置会丢失该关键参数,导致S3无法解析表单数据。冗余字段配置冲突
预签名代码中Fields: { file: '' }属于多余配置,createPresignedPost会自动处理文件字段的签名逻辑,手动添加反而可能引发参数冲突。
修正后的代码
预签名生成代码
import { createPresignedPost } from '@aws-sdk/s3-presigned-post' import { S3Client } from '@aws-sdk/client-s3' import { v4 as uuid } from 'uuid' import { AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY } from '$env/static/private' const s3 = new S3Client({ region: 'us-east-2', credentials: { accessKeyId: AWS_ACCESS_KEY_ID, secretAccessKey: AWS_SECRET_ACCESS_KEY } }) const BUCKET = 'thundermp3-songs' export const GET = async ({ request, url }) => { try { const key = uuid() + '.mp3' const res = await createPresignedPost(s3, { Bucket: BUCKET, Conditions: [ ['content-length-range', 0, 1048576 * 16], // 最大16MB ['starts-with', '$Content-Type', 'audio/mpeg'] // 匹配MP3正确的Content-Type ], Expires: 60 * 60 * 24 * 7, // 7天有效期 Key: key }) const { url: presignedUrl, fields } = res return new Response(JSON.stringify({ fields, key, presignedUrl }), { status: 201 }) } catch(e) { return new Response(JSON.stringify({ message: `Error: ${e}` }), { status: 500 }) } }
前端上传代码
const handleUpload = async () => { if (files.accepted.length === 0) return uploading = true try { const presignedRes = await (await fetch('/api/presign')).json() const file = files.accepted[0] const formData = new FormData() // 添加预签名返回的所有签名字段 Object.entries(presignedRes.fields).forEach(([key, value]) => { formData.set(key, value) }) // 添加待上传的MP3文件 formData.set('file', file) // 移除手动设置的Content-Type,让浏览器自动生成带boundary的请求头 const res = await fetch(presignedRes.presignedUrl, { method: 'POST', body: formData }) // S3成功上传返回204 No Content,不要尝试解析JSON if (res.ok) { console.log('上传成功') } else { // 读取响应文本获取S3详细错误信息 const errorText = await res.text() console.error('上传失败:', errorText) } } catch(e) { console.error('上传错误:', e) } finally { uploading = false handleClear() } }
额外排查建议
- 检查S3存储桶的CORS配置,确保允许POST请求、对应的Origin,以及
Content-Type等必要请求头 - 确认生成预签名的IAM用户拥有
s3:PutObject权限 - 上传前验证文件大小是否在16MB限制内,避免触发
content-length-range条件校验失败
内容的提问来源于stack exchange,提问作者Joel Hager
相关产品推荐
相关产品推荐

