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

Posted

技术标签:

【中文标题】如何使用节点、createPresignedPost 和 fetch 将图像文件直接从客户端上传到 AWS S3【英文标题】:How to upload an image file directly from client to AWS S3 using node, createPresignedPost, & fetch 【发布时间】:2019-10-11 10:29:01 【问题描述】:

我正在使用 s3.createPresignedPost() 在我的服务器上生成一个 AWS S3 预签名的帖子对象。然后,我尝试使用 fetch 使用预签名的 post url 和字段将文件从客户端直接上传到 S3 存储桶,但我得到了一个 403 Forbidden

我尝试手动将表单字段添加到我的 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"); 常量 uuidv4 = 要求(“uuid/v4”); AWS.config.update( accessKeyId:process.env.S3_KEY_ID, secretAccessKey:process.env.S3_SECRET_KEY, 地区:“us-east-1” ); 常量 s3 = 新 S3(); const getPresignedPostData = (bucket, directory) => 常量键 = `$directory/$uuidv4()`; 常量 postData = s3.createPresignedPost( 桶:桶, 字段:键:键,success_action_status:“201”, 条件:[ acl: "public-read" ], 内容类型:“图像/*”, 过期:300 ); 返回后数据; ;

返回类似于:

字段: 密钥:“5cd880a7f8b0480b11b9940c/86d5552b-b713-4023-9363-a9b36130a03f” 政策:Base64 编码的政策字符串 X-Amz 算法:“AWS-HMAC-SHA256” X-Amz 凭据:“AKIAI4ELUSI2XMHFKZOQ/20190524/us-east-1/s3/aws4_request” X-Amz-日期:“20190524T200217Z” X-Amz 签名:“2931634e9afd76d0a50908538798b9c103e6adf067ba4e60b5b54f90cda49ce3” 桶:“图片完美照片” 成功动作状态:“201” , 网址:“https://s3.amazonaws.com/picture-perfect-photos”

我的客户端函数如下所示:

const uploadToS3 = async ( fields, url , file) => 常量 formData = new FormData(); Object.keys(fields).forEach(key => formData.append(key, fields[key])); formData.append("文件", 文件); 尝试 常量配置 = 方法:“POST”, 正文:表格数据 ; 常量响应 = 等待获取(网址,配置); 如果(!response.ok) 抛出新错误(response.statusText); 常量数据 = 等待 response.json(); 返回数据; 捕捉(错误) 控制台.log(err.message); ;

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

*GETPOSTPUT删除*配置>

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

【问题讨论】:

用户是否有权访问存储桶? s3 策略有什么限制吗? 服务器端认证的用户有访问权限。我能够正确地从服务器发布文件。有关详细信息,公共访问如下: 阻止所有公共访问:关闭 阻止对通过新访问控制列表 (ACL) 授予的存储桶和对象的公共访问:关闭 阻止通过任何访问控制列表 (ACL) 授予的对存储桶和对象的公共访问权限:Off 阻止对通过新的公共存储桶策略授予的存储桶和对象的公共访问权限:On 阻止公共和交叉-帐户通过任何公共存储桶策略访问存储桶和对象:开启 @Jonathan 你解决了吗? 【参考方案1】:

我刚刚遇到了同样的问题。

在 S3 控制台中将 <AllowedMethod>PUT</AllowedMethod><AllowedHeader>Content-*</AllowedHeader> 添加到 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>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 错误的原因。

【讨论】:

在哪里可以找到:“S3 控制台中 S3 存储桶的 CORS 规则”?

以上是关于如何使用节点、createPresignedPost 和 fetch 将图像文件直接从客户端上传到 AWS S3的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XQuery 检索父节点?

如何使用 C# 从 XML 节点列表中删除节点

如何匹配文本节点然后使用 XPath 跟随父节点

如何在多个 hadoop 集群中使用一个节点作为数据节点

如何使用 XSL 选择特定元素节点中的所有文本节点?

如何使用Zabbix为NEAR节点发送警告