繁体   English   中英

无法使用节点预签名 url 从 React 将图像文件上传到 s3

[英]Unable to upload image file to s3 from React with Node presigned url

我正在尝试将图像文件上传到 AWS S3 存储桶。 我正在使用 Node/Express 生成预签名的 URL 以将所述图像从 React 前端直接上传到 s3。 问题是我能够将文件作为文件上传(与本地 PC 上的大小相同),但它没有文件扩展名。 也就是说,它只有带符号的字符,例如9c9743b9-4dd7-4afa-af06-c060fb0fb175文件类型,而不是9c9743b9-4dd7-4afa-af06-c060fb0fb175.png文件类型为 png

这是我到目前为止所尝试的简短片段

后端

// My controller
try {
  const payload = await AwsS3Service.getSignedUrlService();
  res.status(200).send({ 
      success: true, 
      data: payload
  });
  ...
const s3 = new aws.S3({
  region: config.awsS3.region,
  accessKeyId: config.awsS3.accessKeyId,
  secretAccessKey: config.awsS3.secretAccessKey,
  signatureVersion: config.awsS3.signatureVersion
})

const getSignedUrlService = async () => {
  const imageName = crypto.randomUUID()

  const params = ({
    Bucket: config.awsS3.bucketName,
    Key: imageName,
    Expires: 120 // in sec
  })

  const uploadUrl = await s3.getSignedUrlPromise('putObject', params)
  return uploadUrl
}

前端

  const [final, setFinal] = useState<FileList | any>('')
  const [img, setImg] = useState<typeof formState.file>(null)

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

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

  const handleFormSubmit: SubmitHandler<IImage> = async (data: IImage) => {
    
    try {
      const signedUrl = await axios({ method: 'GET', url: `${config.API_URL}/awss3` })
      
      const formData = new FormData()
      formData.append('file', final[0])   

      const resUpload = await fetch(`${signedUrl.data.data}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'multipart/form-data' },
        body: final
      })

      if (resUpload.status === 200) {
        // redirect
      }
    } catch(err: any) {
      alert(`An error occured. ${err.message}`)
    }
  }

  return (
    <form onSubmit={e => e.preventDefault()}>
      <Stack>
        <Box>
            <InputLabel htmlFor='imgfile'>Image File</InputLabel>
            <picture>
              { img 
                ? <img src={img} alt='Image preview' height={90} width={160} /> 
                : <Box sx={{ height: 90, width: 160, backgroundColor: '#555' }}></Box>
              }
            </picture>
            <input type='file' accept='image/*' id='imgfile' name='imgFile' onChange={handleImageChange} />
        </Box>
      </Stack>
      <Button type='submit' onClick={handleSubmit(handleFormSubmit)}>
        Submit
      </Button>
    </form>
  )

我已经有一段时间无法解决这个问题了。 任何帮助深表感谢。 感谢您阅读。

将此作为自我回答发布,以防万一有人感兴趣。

我设法解决了这个问题,方法是在将签名请求改为 POST 请求时添加文件名、类型和扩展名,并将文件名和文件类型附加到请求正文中。

// Frontend
const signedUrl = await axios({
    method: 'POST',
    url: `${config.API_URL}/awss3`,
    headers: { 'Content-Type': 'application/json' },
    data: { name: imgName, type: imgType }
})

并将它们添加到Key

// Backend
const getSignedUrlService = async (name, type) => {
  // name and type from req.body
  const imageName = crypto.randomUUID() + '-' + name

  const params = ({
    Bucket: config.awsS3.bucketName,
    Key: imageName,
    ContentType: type,
    Expires: 120 // in sec
  })

  const uploadUrl = await s3.getSignedUrlPromise('putObject', params)
  return uploadUrl
}

感谢所有帮助过的人

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM