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

React通过Node预签名URL上传S3图片丢失文件后缀问题求助

解决S3预签名URL上传丢失文件扩展名问题

问题根源在于后端生成S3对象的Key时仅使用了UUID,未拼接文件扩展名,且前端未将文件的扩展名信息传递给后端,导致S3中存储的文件无后缀,无法被正确识别类型。以下是具体修改方案:

后端修改

  1. 调整预签名URL生成服务,接收前端传来的文件扩展名,拼接在UUID后作为S3对象的Key
  2. 修改接口控制器,获取前端传递的扩展名参数

修改服务层代码

const getSignedUrlService = async (fileExt) => {
  // 拼接UUID与扩展名,生成带后缀的文件名
  const imageName = `${crypto.randomUUID()}.${fileExt}`

  const params = ({
    Bucket: config.awsS3.bucketName,
    Key: imageName,
    Expires: 120 // 有效期(秒)
  })

  const uploadUrl = await s3.getSignedUrlPromise('putObject', params)
  // 可选:返回生成的文件名,方便后续存储到业务数据库
  return {
    uploadUrl,
    key: imageName
  }
}

修改控制器代码

// My controller
try {
  // 从请求Query参数中获取前端传来的文件扩展名
  const { fileExt } = req.query;
  const payload = await AwsS3Service.getSignedUrlService(fileExt);
  res.status(200).send({ 
      success: true, 
      data: payload
  });
  // 其他逻辑...
} catch (err) {
  // 错误处理...
}

前端修改

  1. 请求预签名URL时,将文件的扩展名通过Query参数传递给后端
  2. 修正PUT请求的Content-Type为文件本身的MIME类型,直接上传文件对象(无需FormData)
  3. 修正状态变量的类型定义,避免类型错误

修正状态变量定义

// 原final类型为FileList,实际存储的是单个File对象,修正类型
const [final, setFinal] = useState<File | null>(null)
const [img, setImg] = useState<string | null>(null)

修改文件选择事件处理

const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
  const file = e.target.files?.[0]
  if (!file) return
  
  setFinal(file)
  const { type, name } = file
  setImgName(name)
  setImgType(type)

  const fileReader = new FileReader()
  fileReader.onload = (e) => {
    setImg(e.target?.result as string)
  }
  fileReader.readAsDataURL(file)
}

修改提交事件处理

const handleFormSubmit: SubmitHandler<IImage> = async (data: IImage) => {
  if (!final) return
  
  try {
    // 提取文件扩展名
    const fileExt = final.name.split('.').pop()
    if (!fileExt) {
      alert('无法识别文件扩展名')
      return
    }

    // 请求预签名URL,传递扩展名参数
    const signedUrlRes = await axios({ 
      method: 'GET', 
      url: `${config.API_URL}/awss3`,
      params: { fileExt }
    })
    
    // 直接上传文件对象,Content-Type设为文件自身的MIME类型
    const resUpload = await fetch(signedUrlRes.data.data.uploadUrl, {
      method: 'PUT',
      headers: { 'Content-Type': final.type },
      body: final
    })

    if (resUpload.status === 200) {
      // 可选:将signedUrlRes.data.data.key(带扩展名的文件名)保存到业务数据库
      // 页面跳转逻辑...
    }
  } catch(err: any) {
    alert(`An error occured. ${err.message}`)
  }
}

修改完成后,S3中存储的文件将带有正确的扩展名(如9c9743b9-4dd7-4afa-af06-c060fb0fb175.png),文件类型也会被正确识别。

内容的提问来源于stack exchange,提问作者dEBA M

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 11:20:32