使用预签名 URL 进行上传
本文为希望允许用户上传视频和其他资源 的 Web 应用提供指导。我们建议在服务器端生成一个预签名 URL,允许用户直接将文件上传到您的云存储,而无需通过您的服务器传递文件。
您可以设置诸如最大文件大小和文件类型的约束,应用速率限制,要求身份验证,并预定义存储位置。
为什么使用预签名 URL?
传统的文件上传实现方式是让客户端将文件上传到服务器,然后服务器将文件存储在磁盘上或将上传转发到云存储。虽然这种方法可行,但由于几个原因,它并不理想。
- 减轻负载:如果许多客户端恰好在同一服务器上上传大文件,该服务器可能会因负载过重而变慢甚至崩溃。通过预签名工作流程,服务器只需创建预签名 URL,这比处理文件传输减轻了服务器负载。
- 减少垃圾邮件:为了防止用户将您的上传功能用作免费托管空间,您可以拒绝为他们提供预签名 URL,如果他们超出了您的允许范围。
- 数据安全:由于今天许多托管解决方案是临时的或无服务器的,文件不应存储在它们上面。在服务器重新启动后,不能保证文件仍然存在,您可能会耗尽磁盘空间。
AWS 示例
此示例假定用户上传存储在 S3 中。对于其他前端
首先,在前端接受一个文件,例如使用 <input type="file">
。您应该获得一个 File
,从中可以确定内容类型和内容长度:
App.tsxts
constcontentType =file .type || "application/octet-stream";constarrayBuffer = awaitfile .arrayBuffer ();constcontentLength =arrayBuffer .byteLength ;
App.tsxts
constcontentType =file .type || "application/octet-stream";constarrayBuffer = awaitfile .arrayBuffer ();constcontentLength =arrayBuffer .byteLength ;
此示例使用 @aws-sdk/s3-request-presigner
和 从 @remotion/lambda
导入的 AWS SDK。通过调用下面的函数,将生成两个 URL:
presignedUrl
是文件可以上传到的 URLreadUrl
是可以从中读取文件的 URL。
api/generate-presigned-url.tstsx
import {getSignedUrl } from "@aws-sdk/s3-request-presigner";import {AwsRegion ,getAwsClient } from "@remotion/lambda/client";import {v4 } from "uuid";export constgeneratePresignedUrl = async (contentType : string,contentLength : number,expiresIn : number,bucketName : string,region :AwsRegion ,):Promise <{presignedUrl : string;readUrl : string }> => {if (contentLength > 1024 * 1024 * 200) {throw newError (`File may not be over 200MB. Yours is ${contentLength } bytes.`,);}const {client ,sdk } =getAwsClient ({region :process .env .REMOTION_AWS_REGION asAwsRegion ,service : "s3",});constkey =v4 ();constcommand = newsdk .PutObjectCommand ({Bucket :bucketName ,Key :key ,ACL : "public-read",ContentLength :contentLength ,ContentType :contentType ,});constpresignedUrl = awaitgetSignedUrl (client ,command , {expiresIn ,});// The location of the assset after the uploadconstreadUrl = `https://${bucketName }.s3.${region }.amazonaws.com/${key }`;return {presignedUrl ,readUrl };};
api/generate-presigned-url.tstsx
import {getSignedUrl } from "@aws-sdk/s3-request-presigner";import {AwsRegion ,getAwsClient } from "@remotion/lambda/client";import {v4 } from "uuid";export constgeneratePresignedUrl = async (contentType : string,contentLength : number,expiresIn : number,bucketName : string,region :AwsRegion ,):Promise <{presignedUrl : string;readUrl : string }> => {if (contentLength > 1024 * 1024 * 200) {throw newError (`File may not be over 200MB. Yours is ${contentLength } bytes.`,);}const {client ,sdk } =getAwsClient ({region :process .env .REMOTION_AWS_REGION asAwsRegion ,service : "s3",});constkey =v4 ();constcommand = newsdk .PutObjectCommand ({Bucket :bucketName ,Key :key ,ACL : "public-read",ContentLength :contentLength ,ContentType :contentType ,});constpresignedUrl = awaitgetSignedUrl (client ,command , {expiresIn ,});// The location of the assset after the uploadconstreadUrl = `https://${bucketName }.s3.${region }.amazonaws.com/${key }`;return {presignedUrl ,readUrl };};
Explanation:
- 首先,上传请求会被检查约束条件。在这个例子中,我们拒绝超过 200MB 的上传。您可以添加更多约束条件或添加速率限制。
- 使用 getAwsClient() 导入 AWS SDK。如果您不使用 Remotion Lambda,请直接安装
@aws-sdk/client-s3
包。 - 使用 UUID 作为文件名以避免名称冲突。
- 最后,计算并返回预签名 URL 和输出 URL。
使用预签名 URL
将预签名 URL 发送回客户端。之后,您现在可以使用内置的 fetch()
函数执行上传:
App.tsxts
awaitfetch (presignedUrl , {method : "PUT",body :arrayBuffer ,headers : {"content-type":contentType ,},});
App.tsxts
awaitfetch (presignedUrl , {method : "PUT",body :arrayBuffer ,headers : {"content-type":contentType ,},});