You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Lambda的AWS S3文件夹压缩下载超时问题技术问询

问题解决:Lambda处理S3大文件ZIP下载超时

你的问题核心在于把整个压缩流加载到内存并转换为Base64字符串,大文件场景下不仅会耗尽Lambda内存,还会因为处理时间过长触发超时。再加上当前串行拉取S3对象的逻辑,进一步拉长了执行时间。

核心原因

  • 内存过载:streamToString会把压缩包的所有chunk存在数组里,最后拼接成Buffer再转Base64,大文件直接占满Lambda内存,处理速度骤降。
  • 串行IO阻塞:循环里逐个await client.s3client.send(command),每个文件都要等上一个下载完才能开始,大文件夹下耗时指数级增长。
  • Base64额外开销:Base64编码会让数据体积增加33%,进一步加剧内存和时间压力。

解决方案

方案1:流式输出到API Gateway(推荐,直接返回下载流)

如果用的是AWS HTTP API,开启响应流式传输,直接把压缩流输出给客户端,不需要把整个文件加载到内存。

修改压缩逻辑,直接将archiver的流pipe到Lambda响应流:

// 假设Lambda使用HTTP API的流式响应
export const handler = async (event, context) => {
  const archive = archiver('zip', { zlib: { level: 9 } });
  
  // 并行获取S3对象,减少等待时间
  const getObjectPromises = keys.map(key => {
    const command = new GetObjectCommand({ Bucket: client.bucket, Key: key });
    return client.s3client.send(command).then(res => ({ key, body: res.Body }));
  });
  
  const objects = await Promise.all(getObjectPromises);
  
  objects.forEach(({ key, body }) => {
    archive.append(body, { name: key });
  });
  
  archive.finalize();
  
  // 返回流式响应
  return {
    statusCode: 200,
    headers: {
      'Content-Type': 'application/zip',
      'Content-Disposition': 'attachment; filename="s3-files.zip"'
    },
    isBase64Encoded: false,
    body: archive
  };
};

注意:需要在Lambda函数配置里开启「响应流式传输」,同时HTTP API要支持流式响应。

方案2:先把ZIP上传到S3,再返回下载链接

如果无法用流式响应,就把压缩后的ZIP上传到S3存储桶,给用户返回预签名下载链接,Lambda不需要处理大响应:

import { PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";

export const handler = async (event) => {
  const archive = archiver('zip', { zlib: { level: 9 } });
  const zipKey = `downloads/${Date.now()}-s3-files.zip`;
  
  // 创建S3上传流
  const uploadParams = {
    Bucket: client.bucket, // 可使用单独的存储桶
    Key: zipKey,
    ContentType: 'application/zip'
  };
  const uploadStream = client.s3client.putObject(uploadParams);
  
  // 将压缩流pipe到S3上传流
  archive.pipe(uploadStream);
  
  // 并行拉取S3对象并添加到压缩包
  const getObjectPromises = keys.map(key => {
    const command = new GetObjectCommand({ Bucket: client.bucket, Key: key });
    return client.s3client.send(command).then(res => ({ key, body: res.Body }));
  });
  
  const objects = await Promise.all(getObjectPromises);
  objects.forEach(({ key, body }) => archive.append(body, { name: key }));
  
  await archive.finalize();
  await uploadStream.done();
  
  // 生成1小时有效期的预签名下载链接
  const signedUrl = await client.s3client.getSignedUrl(new GetObjectCommand({
    Bucket: client.bucket,
    Key: zipKey
  }), { expiresIn: 3600 });
  
  return {
    statusCode: 200,
    body: JSON.stringify({ downloadUrl: signedUrl })
  };
};

方案3:临时优化(仅适用于中等大小文件)

如果必须用Base64返回,至少优化串行IO和资源配置:

  1. 用Promise.all并行拉取S3对象,替代串行循环。
  2. 调高Lambda内存配额(内存越高,CPU配额越高),同时延长超时时间(最大15分钟)。
    但这个方案本质还是把文件加载到内存,大文件依然会出问题,不推荐长期使用。

关键优化点

  • 删掉streamToString,不要把整个压缩流转成字符串,直接用流式处理。
  • 用Promise.all并行处理S3对象拉取,减少IO等待时间。
  • 避免在Lambda内存中存储大文件,要么流式输出,要么转存到S3。

内容的提问来源于stack exchange,提问作者Mohit jassal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:11:00