简体   繁体   English

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

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

I am trying to upload an image file to an AWS S3 bucket.我正在尝试将图像文件上传到 AWS S3 存储桶。 I am using Node/Express to generate a presigned URL to upload said image directly to s3 from the React frontend.我正在使用 Node/Express 生成预签名的 URL 以将所述图像从 React 前端直接上传到 s3。 The thing is I am able to upload the file as a file (same size as on local PC) but it does not have the file extension.问题是我能够将文件作为文件上传(与本地 PC 上的大小相同),但它没有文件扩展名。 That is, it has only the signed character eg 9c9743b9-4dd7-4afa-af06-c060fb0fb175 with file type of file instead of 9c9743b9-4dd7-4afa-af06-c060fb0fb175.png with png file type也就是说,它只有带符号的字符,例如9c9743b9-4dd7-4afa-af06-c060fb0fb175文件类型,而不是9c9743b9-4dd7-4afa-af06-c060fb0fb175.png文件类型为 png

Here is a brief snippet of what I have tried so far这是我到目前为止所尝试的简短片段

Backend后端

// 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
}

Frontend前端

  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>
  )

I have been unable to solve this for a while now.我已经有一段时间无法解决这个问题了。 Any help is much appreciated.任何帮助深表感谢。 Thank you for reading.感谢您阅读。

Posting this as a self-answer just in case anyone is interested.将此作为自我回答发布,以防万一有人感兴趣。

I managed to work around this by adding the file name, type, and extension when making the sign request as a POST request instead and attaching the file name and file type in the request body.我设法解决了这个问题,方法是在将签名请求改为 POST 请求时添加文件名、类型和扩展名,并将文件名和文件类型附加到请求正文中。

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

And added them in the Key并将它们添加到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
}

Thanks to everyone who helped感谢所有帮助过的人

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

相关问题 使用预签名 URL 将文件放入 S3 - PUT file to S3 with presigned URL 如何使用 postdata preSigned Url 调用 Amazon S3 存储桶以使用空手道上传文件 - How to call Amazon S3 bucket using postdata preSigned Url to upload a file using Karate 如何使用预签名 url 在同一订单中为 Amazon S3 上传文件使用表单字段 - How to use form fields in the same order for Amazon S3 upload file using presigned url 使用带有 VichUploader 的 S3 预签名 URL - Use S3 presigned URL with VichUploader 如何将图像文件从 s3 存储桶上传到 cloudinary (nodejs) - How to upload an image file from an s3 bucket to cloudinary (nodejs) 无法从 node.js 中的 S3 获取文件 - Unable to fetch file from S3 in node js 处理 aws s3 预签名 URL 过期日期的最佳实践 - Best practice to handle aws s3 presigned URL expiration date 使用 AWS Amplify 从 React Native 上传到 S3 - Upload to S3 from React Native with AWS Amplify 使用来自 flutter 应用程序的 createPresignedPost 将图像上传到 S3 - Upload image to S3 using createPresignedPost from flutter app 如何使用 boto3 资源而不是客户端生成 S3 预签名 URL - How to generate S3 presigned URL with boto3 resource instead of client
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM