如何使用node,createPresignedPost和fetch从客户端直接将图像文件上传到AWS S3

时间:2019-05-24 20:20:36

标签: node.js amazon-s3 fetch

我正在使用s3.createPresignedPost()在服务器上生成一个AWS S3预签名发布对象。然后,我尝试使用预签名的发布网址和字段通过访存直接从客户端将文件直接上传到S3存储桶,但是却得到了s3.createPresignedPost()

我尝试将表单字段手动添加到FormData对象中以直接匹配以下示例:https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html,但继续收到403错误。

用于生成发布对象的服务器端功能


    const AWS = require("aws-sdk/global");
    const S3 = require("aws-sdk/clients/s3");
    const uuidv4 = require("uuid/v4");

    AWS.config.update({
      accessKeyId: process.env.S3_KEY_ID,
      secretAccessKey: process.env.S3_SECRET_KEY,
      region: "us-east-1"
    });

    const s3 = new S3();

    const getPresignedPostData = (bucket, directory) => {
      const key = `${directory}/${uuidv4()}`;
      const postData = s3.createPresignedPost({
        Bucket: bucket,
        Fields: { Key: key, success_action_status: "201" },
        Conditions: [{ acl: "public-read" }],
        ContentType: "image/*",
        Expires: 300
      });
      return postData;
    };

返回如下所示的内容:


    {
      fields: {
        Key: "5cd880a7f8b0480b11b9940c/86d5552b-b713-4023-9363-a9b36130a03f"
        Policy: {Base64-encoded policy string}
        X-Amz-Algorithm: "AWS-HMAC-SHA256"
        X-Amz-Credential: "AKIAI4ELUSI2XMHFKZOQ/20190524/us-east-1/s3/aws4_request"
        X-Amz-Date: "20190524T200217Z"
        X-Amz-Signature: "2931634e9afd76d0a50908538798b9c103e6adf067ba4e60b5b54f90cda49ce3"
        bucket: "picture-perfect-photos"
        success_action_status: "201"
      },
      url: "https://s3.amazonaws.com/picture-perfect-photos"
    }

我的客户端功能如下:



    const uploadToS3 = async ({ fields, url }, file) => {
        const formData = new FormData();
        Object.keys(fields).forEach(key => formData.append(key, fields[key]));
        formData.append("file", file);

        try {
          const config = {
            method: "POST",
            body: formData
          };
          const response = await fetch(url, config);

          if (!response.ok) {
            throw new Error(response.statusText);
          }

          const data = await response.json();
          return data;
        } catch (err) {
          console.log(err.message);
        }
      };

我的S3存储桶CORS配置如下:


    <?xml version="1.0" encoding="UTF-8"?>
    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
    </CORSConfiguration>

我希望获得设置403 Forbidden时发送的XML文档,但仍在不断获取success_action_status: "201"

1 个答案:

答案 0 :(得分:0)

我只是遇到了同样的问题。

在S3控制台的S3存储桶的CORS规则中添加<AllowedMethod>PUT</AllowedMethod><AllowedHeader>Content-*</AllowedHeader>

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedHeader>Content-*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

向服务器发出发布请求,以获取预先签名的S3 URL。发布请求的正文中应包含文件名和mime类型:

快速路线:

app.post("/s3-signed-url",async (req, res, next)=>{
    const s3 = new AWS.S3();
    const url = await s3.getSignedUrlPromise('putObject', {
        Bucket: "BUCKET_NAME",
        Key: req.body.name,
        ContentType: req.body.type,
        Expires: 60,
        ACL: 'public-read',
    });
    res.json({signedUrl: url})
});

选择要上传的文件时,异步功能中的客户端代码:

async function onFileDrop(file){
    const {name, type} = file; // I use react-dropzone to obtain the file.
    const options = {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name,type})
    }
    const rawResponse = await fetch("/s3-signed-url", options)
    const {signedUrl} = await rawResponse.json();

    // After you obtain the signedUrl, you upload the file directly as the body.
    const uploadOptions = { method: 'Put', body: file,}
    const res = await fetch(signedUrl, uploadOptions);
    if(res.ok) {
        return res.json()
    }
}

我的致命错误是,在上传带有签名URL的文件时,我在uploadOptions中添加了多余的标头。我遇到了其他线程,这些线程声称我必须显式添加“ Content-Type”标头:

`const wrongUploadOptions = { method: 'Put', body: file, headers:{"Content-Type": file.type, "x-amz-acl": public-read}}`

但是对于我而言,这完全没有必要,这就是我收到403错误的原因。