首页>>前端>>JavaScript->用Node版aws

用Node版aws

时间:2023-11-30 本站 点击:0

项目在使用aws的s3对象存储服务,需要后端将请求预签名后传给前端,前端拿这些签名信息跟s3交互。由于我没搞过s3的签名,需要花时间去调研官方的sdk等资料,花了一天时间终于搞出来了,碰了很多坑,在这里记录一下。

不要自己做签名

首先,AWS提供了签名的步骤和签名的组成信息,很多人以为不难,想要自己实现。其实非常不推荐,因为他们的文档只是做参考,让读者对签名的原理有个大体的认知,并不是严格的签名教程。

如果你真的要这么做,会发现出错率非常高,最常见的是签名不一致,你提供的签名跟AWS根据你负载计算而得到的签名不匹配,并且AWS没有帮你定位出错点,他只告诉你你错了。最终的结果就是你排查了半天也找不到原因所在。我就是在这里浪费了太多时间。

不过坑只有踩了你才知道这是坑,这也是一种收获,不是吗?

推荐用sdk来做签名,轻松又简单!

用SDK做预签名

首先需要安装一些依赖包,这里采用本文发稿时的最新版aws-sdk

@aws-sdk/client-s3

@aws-sdk/s3-request-presigner

第一个是s3的客户端sdk,第二个是预签名请求的sdk

首先做s3客户端初始化工作:

const{S3Client}=require("@aws-sdk/client-s3");constREGION="****";//e.g."us-east-1"//CreateanAmazonS3serviceclientobject.consts3Client=newS3Client({region:REGION,credentials:{accessKeyId:"******",secretAccessKey:"**************",}});

单文件上传签名示例

写一个/upload-info的端点给前端做签名调用

//引入相关模块const{PutObjectCommand,}=require("@aws-sdk/client-s3");const{getSignedUrl}=require("@aws-sdk/s3-request-presigner");app.get("/upload-info",(req,res,next)=>{//初始化命令实体constputCmd=newPutObjectCommand({Bucket:"****",Key:"image.jpg"});//获取签名getSignedUrl(s3Client,putCmd,{expiresIn:3600}).then((url)=>{//将签名好的url回传给前台res.send(url);next();});});

前端获取并使用签名请求

request({url:`http://localhost:8080/upload-info`,}).then((signedUrl)=>{request({url:signedUrl,method:"put",data:file,}).then((res)=>{console.log(`singlefileuploadsucceed!`);});});

分段上传签名示例

写一个/upload-part端点给前端做签名调用

//引入模块const{CreateMultipartUploadCommand,CompleteMultipartUploadCommand,UploadPartCommand,}=require("@aws-sdk/client-s3");const{getSignedUrl}=require("@aws-sdk/s3-request-presigner");app.get("/upload-part",(req,res,next)=>{//objectkeyoffile(含文件在s3桶的目录结构,eg:`dir/book.pdf`)constKey='book.pdf'//countofpartsconstlength=req.query.count;constcreateMultiUpload=s3Client.send(newCreateMultipartUploadCommand({Bucket:"***",Key,}));constgetCompleteUrl=(UploadId)=>{returngetSignedUrl(s3Client,newCompleteMultipartUploadCommand({Bucket:"***",Key,UploadId,}),{expiresIn:3600});};constprePromise=createMultiUpload.then((result)=>{const{Key,UploadId}=result;returngetCompleteUrl(UploadId).then((completeUrl)=>{return{Key,UploadId,CompleteUrl:completeUrl,};});});prePromise.then((result)=>{const{Key,UploadId,CompleteUrl}=result;constsignPartPromsArr=[];for(letPartNumber=1;PartNumber<=length;PartNumber++){constcmd=newUploadPartCommand({Bucket:"***",Key,PartNumber,UploadId,});signPartPromsArr.push(getSignedUrl(s3Client,cmd,{expiresIn:3600}));}//获取所有分片的签名URLPromise.all(signPartPromsArr).then((partsUrlArr)=>{//签名全部结束,开始给前端回传res.send({partEndpoints:partsUrlArr,CompleteUrl,//belowtwoisnotnecessaryKey,UploadId,});next();});});});

所以可以看到,整个签名包括三部分:

通过sdk创建分段上传,获取上传Id

根据桶、对象的Key、分段编号上传Id签名每一个分段端点

根据上传Id签名完成分段上传的端点(通知s3进行分段合并)

其中前段获得的两种签名:

每一个分段的签名端点

完成分段上传的签名端点

至于前端的使用示例不再赘述,跟单文件使用本质一致的,只是细节多一点:

最小切片size不可小于5MB(s3的硬性规定)

并发数量的控制

重试机制

进度的提示

所有分片上传完毕,需要将每段的编号和Etag(从responseHeader获取),按照从小到大的顺序拼成一个XML文本通过另一个签名端点(完成分段上传端点)发送给S3

最后

感谢阅读,如有任何问题,欢迎留言讨论!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/3925.html