如何利用S3预签名URL上传外部文件URL对应的文件至S3?
问题解决:上传外部图片到S3出现空对象
问题原因
fetch返回的res.body是ReadableStream,直接作为PUT请求的body传给axios时,axios无法正确解析这个流,导致上传内容为空。- 前端设置
Access-Control-Allow-Origin请求头无效,这个是服务端返回的响应头,无需在PUT请求中携带。
修复方案
1. 将响应流转换为Blob(推荐)
把fetch获取到的响应转换为Blob格式,再作为请求体上传:
fetch(imageUrl) .then(async (res) => { // 将响应流转换为Blob const imageBlob = await res.blob(); const { data } = await axios.post('api/create-upload-url', { key: filename, bucket: bucketName, // 建议从响应头获取真实的Content-Type,避免硬编码不匹配 contentType: res.headers.get('content-type') || 'image/jpg', }) await axios.put(data.url, imageBlob, { headers: { // 必须和预签名URL生成时指定的contentType完全一致 'Content-Type': res.headers.get('content-type') || 'image/jpg', }, }) })
2. 替代方案:转换为ArrayBuffer
如果需要用二进制数组格式,也可以这样处理:
fetch(imageUrl) .then(async (res) => { const imageBuffer = await res.arrayBuffer(); const { data } = await axios.post('api/create-upload-url', { key: filename, bucket: bucketName, contentType: res.headers.get('content-type') || 'image/jpg', }) await axios.put(data.url, imageBuffer, { headers: { 'Content-Type': res.headers.get('content-type') || 'image/jpg', }, }) })
关键注意事项
- 预签名URL生成时指定的
contentType必须和PUT请求的Content-Type头完全匹配,否则S3会拒绝请求或导致上传内容异常。 - S3的跨域配置需要在桶的CORS规则中添加允许PUT请求的来源,而非前端设置请求头。
内容的提问来源于stack exchange,提问作者grabury
相关产品推荐
相关产品推荐

