如何使用Axios或XHR通过预签名S3 PUT URL上传二进制图片?
如何使用Axios或XHR通过预签名S3 PUT URL上传二进制图片?
现有代码与测试情况
1. 生成S3预签名URL的代码
const file = req.files ? req.files.filename || null : null const extension = path.extname(file.name) const client = new S3Client() const params = { Bucket: S3_BUCKET, // 环境变量 Key: `tmp/photos/image_file.png`, ContentDisposition: 'inline', ContentType: file.mimetype, Body: file.data } const command = new PutObjectCommand(params) const presignedS3Url = await getSignedUrl(client, command, { expiresIn: 3600 })
可正常工作的CURL请求
curl --location --request PUT $presignedS3Url \ --header 'Content-Disposition: inline' \ --header 'Content-Type: image/png' \ --data-binary '@/Users/name/Downloads/image-file.png'
失败的Axios尝试代码
const imageFile = fs.readFile('/Users/filepath/image-file.png') const bufferString = Buffer.from(imageFile, 'base64') await axios.put(presignedS3Url, { data: bufferString }, { headers: { 'Content-Type': file.mimetype, 'Content-Disposition': 'inline' } })
解决方案
修复Axios上传代码
你的Axios代码存在两个关键问题:
fs.readFile是异步方法,直接赋值会得到Promise而非文件内容,需改用同步读取或异步等待;- 上传时无需把数据包装在
{ data: bufferString }里,直接传递二进制数据作为请求体即可。
修复后的同步读取版本:
const imageBuffer = fs.readFileSync('/Users/filepath/image-file.png') await axios.put(presignedS3Url, imageBuffer, { headers: { 'Content-Type': 'image/png', // 需与预签名URL生成时的ContentType一致 'Content-Disposition': 'inline' } })
异步读取版本:
const imageBuffer = await fs.promises.readFile('/Users/filepath/image-file.png') await axios.put(presignedS3Url, imageBuffer, { headers: { 'Content-Type': 'image/png', 'Content-Disposition': 'inline' } })
使用XHR上传的方法
如果用原生XHR实现,代码示例如下:
const xhr = new XMLHttpRequest() xhr.open('PUT', presignedS3Url, true) xhr.setRequestHeader('Content-Type', 'image/png') xhr.setRequestHeader('Content-Disposition', 'inline') // 以浏览器input文件为例 const fileInput = document.querySelector('input[type="file"]') const file = fileInput.files[0] xhr.send(file) // 监听上传状态 xhr.onload = function() { if (xhr.status === 200) { console.log('上传成功') } else { console.error('上传失败', xhr.responseText) } }
关键注意事项
- 预签名URL生成时指定的
ContentType必须和上传请求头的Content-Type完全一致,S3会严格验证这一点; - 上传的二进制数据需为原始文件内容,无需转base64(除非预签名时明确指定了对应编码);
- 浏览器环境下使用Axios时,直接传递File对象或ArrayBuffer作为请求体即可,无需处理Buffer。
内容的提问来源于stack exchange,提问作者kush patel
相关产品推荐
相关产品推荐

