- 示例工程
【免费下载链接】examples
Serverless Examples – A collection of boilerplates and examples of serverless architectures built with the Serverless Framework on AWS Lambda, Microsoft Azure, Google Cloud Functions, and more.
本文基于 Serverless Examples 仓库中的 aws-node-fetch-file-and-store-in-s3 示例,讲解如何用一个 Node.js Lambda 函数从远程 URL 抓取文件(如图片),再通过 AWS SDK v3 的PutObjectCommand将其上传到 S3 存储桶。读完本文,你可以掌握:完整的serverless.yml配置(含 IAM 权限、自动创建的 S3 资源)、基于 ESM 的 Lambda 处理函数实现细节,以及部署(serverless deploy)和本地调用(serverless invoke)的完整操作流程。
适用场景与整体思路
该示例的定位很明确——从远程来源(URL)获取文件,然后上传到 S3 存储桶。README 中给出的典型用例是:
- 保存来自其他服务的用户头像(Store a user's profile picture from another service)。
实际项目中这类需求非常普遍:用户在注册时只提供了一个第三方头像 URL(例如某个 CDN 或社交媒体头像链接),如果你的业务只希望引用自己 S3 桶内的资源(避免第三方链接失效、跨域问题或内容被篡改),就需要一个"中转"步骤把文件抓取下来并持久化到自己的存储桶里。
整个流程只有三步:
- Lambda 函数接收事件,其中包含
image_url(要抓取的远程文件地址)和key(上传到桶内使用的对象 Key); - 函数用内置的
fetch拉取远程数据,转成Buffer; - 调用 S3 的
PutObjectCommand把Buffer作为Body写入桶中。
README 给出的核心逻辑就是这几行:
const response = await fetch(event.image_url); const buffer = Buffer.from(await response.arrayBuffer()); await s3.send(new PutObjectCommand({ Bucket, Key, Body: buffer }));下面结合仓库中的真实源码 handler.js 和 serverless.yml 逐层展开。
处理函数实现:handler.js 逐行解析
完整的 handler.js 内容如下:
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({}); export const save = async (event) => { const response = await fetch(event.image_url); if (!response.ok) { throw new Error(`Failed to fetch ${response.url}: ${response.status} ${response.statusText}`); } const buffer = Buffer.from(await response.arrayBuffer()); return s3.send( new PutObjectCommand({ Bucket: process.env.BUCKET, Key: event.key, Body: buffer, }) ); };这里有几个值得注意的实现细节:
基于 ESM 的模块写法
文件使用import导入 AWS SDK v3,并且处理函数用export const save导出。这与 package.json 中的"type": "module"声明相配合——Lambda 会把它当作 ES Module 加载。对应的,serverless.yml 中 handler 的写法是handler: handler.save,即指向handler.js中名为save的命名导出函数。
使用全局 fetch 抓取远程文件
代码直接调用全局fetch(event.image_url),没有引入node-fetch、axios等任何额外的 HTTP 库。这依赖 Node.js 18+ 内置的undici全局fetch。从 serverless.yml 可以看到运行时声明为runtime: nodejs24.x,因此该写法在当前仓库示例中是成立的;如果把代码搬回 Node 16 或更低版本,就需要自行引入第三方 fetch 实现。
抓取后先用response.ok判断 HTTP 状态,非 2xx/3xx 时抛出包含 URL、状态码和状态文本的错误;成功后通过response.arrayBuffer()拿到二进制内容,再用Buffer.from(...)转成 Node.js 的Buffer,作为 S3 上传的Body。
S3Client 实例在模块顶层创建
s3客户端在模块加载时就实例化了(const s3 = new S3Client({})),而不是在每次调用时新建。AWS SDK v3 的客户端对象是无状态、可复用的,放在顶层可以复用连接池,是 Lambda 冷启动优化的常见做法。注意new S3Client({})没有传region等显式配置——它会依赖 Lambda 运行时的默认凭据链(从执行角色读取)和环境变量中自动解析区域,因此这个函数能写入桶,前提条件是执行角色被授予了相应权限,这一点由serverless.yml中的 IAM 配置保证,见下一节。
事件入参与桶名注入
函数从event.image_url和event.key读取入参,桶名则来自环境变量process.env.BUCKET——不在代码里硬编码桶名,而是由部署时注入(桶名是 CloudFormation 生成的全局唯一名称)。这与部署输出中Deploying "fetch-file-and-store-in-s3" to stage "dev" (us-west-1)的场景一致。
serverless.yml:资源、权限与环境变量
serverless.yml 的完整内容:
service: fetch-file-and-store-in-s3 frameworkVersion: '4' provider: name: aws runtime: nodejs24.x architecture: arm64 stage: dev region: us-west-1 iam: role: statements: - Effect: Allow Action: - s3:PutObject - s3:PutObjectAcl Resource: !Sub "${UploadBucket.Arn}/*" functions: save: handler: handler.save environment: BUCKET: !Ref UploadBucket resources: Resources: UploadBucket: Type: AWS::S3::Bucket这份配置覆盖了示例能"零手工配置直接部署"所需的全部要素:
provider:AWS 平台、nodejs24.x运行时、arm64架构(Graviton,按 vCPU/GB 计费更划算且性能更强)、dev阶段、us-west-1区域。- IAM 权限:通过
provider.iam.role.statements给默认执行角色追加一条策略——只允许s3:PutObject和s3:PutObjectAcl两个动作,且资源限定为!Sub "${UploadBucket.Arn}/*",即该桶下的所有对象(object)。这遵循最小权限原则:函数没有读取、删除桶内对象的权限,也没有其他服务的权限。 - 环境变量:
BUCKET: !Ref UploadBucket把 CloudFormation 资源UploadBucket的逻辑引用(即生成的真实桶名)注入 Lambda 的环境变量,与handler.js中的process.env.BUCKET对应。 - 自定义资源:
resources.Resources中声明了一个AWS::S3::Bucket(逻辑 ID 为UploadBucket)。README 强调:存储桶会作为这个 stack 的一部分被自动创建,桶名由 CloudFormation 生成的全局唯一名称,无需任何手工创建。
也就是说,这个示例唯一的第三方依赖就是@aws-sdk/client-s3,见 package.json:
"dependencies": { "@aws-sdk/client-s3": "^3.700.0" }部署与调用
安装依赖与部署
先安装依赖:
npm install然后部署:
serverless deploy部署完成的预期输出类似:
Deploying "fetch-file-and-store-in-s3" to stage "dev" (us-west-1) ✔ Service deployed to stack fetch-file-and-store-in-s3-dev (42s) functions: save: fetch-file-and-store-in-s3-dev-save (1.2 kB)可以看到函数名会被生成为<service>-<stage>-<function>(fetch-file-and-store-in-s3-dev-save),函数打包后只有 1.2 kB(因为唯一的依赖@aws-sdk/client-s3会在打包时一并带上,这里的 1.2 kB 是 handler 自身大小展示)。
用 serverless invoke 本地触发测试
部署后可以直接用serverless invoke向函数发送事件测试:
serverless invoke --function save --log --data='{ "image_url": "https://assets-cdn.github.com/images/modules/open_graph/github-mark.png", "key": "github.png"}'预期输出类似:
"S3 saved"以及 Lambda 的执行报告:
START RequestId: c658859d-bd11e6-ac1f-c7a7ee5bd7f3 Version: $LATEST END RequestId: c658859d-bd11e6-ac1f-c7a7ee5bd7f3 REPORT RequestId: c658859d-bd11e6-ac1f-c7a7ee5bd7f3 Duration: 436.94 ms Billed Duration: 500 ms Memory Size: 1024 MB Max Memory Used: 29 MB函数返回值是s3.send(...)的 Promise,即PutObjectCommand的响应结果(README 展示的是早期 SDK 响应的"Saved"形态;从当前仓库源码看,返回值就是PutObjectCommand的完整响应对象)。调用完成后,可以去 CloudWatch Logs 查看日志,或直接到 S3 控制台检查github.png这个对象是否已存在于部署时创建的桶中。
扩展阅读:Lambda 并发上限与扩容
README 的 Scaling 一节指出:默认情况下,AWS Lambda 对同一区域内所有函数的总并发执行数限制为 100。这个默认限制是一个安全阀,用于在开发和测试初期保护你免于因失控或递归函数产生的意外成本。如果业务增长需要更高的并发,可以通过 AWS 控制台提交"请求提高并发执行上限"(Request a limit increase for concurrent executions)来调整账户级并发配额。
对这个"抓文件存 S3"的用法而言,并发上限通常不是瓶颈——真正的约束往往是远程 URL 的响应速度和单文件体积(整个文件会被读进内存再上传),在生产环境里还应考虑超时、重试与文件大小校验等问题,这些在当前示例中均未处理,属于按需自行加固的扩展点。
小结
该示例用不到 20 行的 handler.js 和一份 29 行的 serverless.yml,完整演示了 Serverless Framework v4 下"远程文件 → Lambda → S3"这一经典数据落盘链路:
- 全局
fetch+Buffer完成远程二进制抓取; @aws-sdk/client-s3的PutObjectCommand完成上传;- IAM 语句把权限收敛到目标桶对象级;
AWS::S3::Bucket自定义资源 +!Ref/!Sub实现桶的自动创建与名称注入,避免硬编码。
如需进一步学习同仓库中其他"文件入 S3"的变体,可以参考 aws-node-s3-file-replicator(S3 事件驱动的桶间复制)与 aws-node-signed-uploads(签名直传)。
- 示例工程
【免费下载链接】examples
Serverless Examples – A collection of boilerplates and examples of serverless architectures built with the Serverless Framework on AWS Lambda, Microsoft Azure, Google Cloud Functions, and more.
相关推荐
Serverless Examples:用 DynamoDB Streams + AWS Lambda 将表变更归档到 S3 的 Node.js 实战
Serverless Examples:用 DynamoDB Streams + AWS Lambda 将表变更归档到 S3 的 Node.js 实战 本篇基于
示例工程Serverless Examples 动态图片缩放 API 实战:用 AWS Lambda、S3 与 sharp 实现按需图片缩放
Serverless Examples 动态图片缩放 API 实战:用 AWS Lambda、S3 与 sharp 实现按需图片缩放 本文基于 Serverle
示例工程Serverless Examples:用 AWS Lambda Layer 打包 FFmpeg,构建 S3 触发的 GIF 生成服务
Serverless Examples:用 AWS Lambda Layer 打包 FFmpeg,构建 S3 触发的 GIF 生成服务 本文以 Serverle
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考