AWS Lambda:Python 3 中用 wget 下载文件并上传到 S3
在 Lambda 里下载一个大文件再存到 S3,听起来很简单——但如果你直接 pip install requests 塞进 zip 包,或者用 Lambda 层,会发现麻烦得不行。
其实 AWS Lambda 的基础镜像里已经自带了 wget 和 curl,根本不用装任何第三方库。这篇教你用纯 Python + subprocess 搞定下载 + 上传到 S3 的全过程。
为什么用 wget 而不是 requests/boto3?
Lambda 的 Docker 基础镜像(Amazon Linux 2023)已经内置了 wget、curl、tar 等常用命令行工具。好处:
- 零依赖——不用装 requests、urllib3 等第三方包,zip 包体积小
- 速度快——wget 底层用 C 实现,下载大文件比纯 Python 快
- 自带断点续传——wget 有
-c参数,网络中断可以继续 - Lambda 层都不用——不需要搞 Lambda Layer 来装依赖
当然,上传到 S3 还是用 boto3,因为 boto3 是 Lambda 运行时自带的,不需要额外安装。
完整代码
直接上代码,后面再逐段讲:
import os
import subprocess
import boto3
import json
import logging
from uuid import uuid4
logger = logging.getLogger()
logger.setLevel(logging.INFO)
DOWNLOAD_URL = os.environ.get("DOWNLOAD_URL", "https://example.com/largefile.zip")
S3_BUCKET = os.environ.get("S3_BUCKET", "my-bucket")
S3_PREFIX = os.environ.get("S3_PREFIX", "downloads")
LOCAL_PATH = "/tmp/downloaded_file"
def lambda_handler(event, context):
# 1. 下载文件
logger.info(f"开始下载: {DOWNLOAD_URL}")
download_file(DOWNLOAD_URL, LOCAL_PATH)
# 2. 上传到 S3
logger.info("开始上传到 S3")
s3_key = f"{S3_PREFIX}/{os.path.basename(DOWNLOAD_URL)}"
upload_to_s3(LOCAL_PATH, S3_BUCKET, s3_key)
return {
"statusCode": 200,
"body": json.dumps({
"message": "下载并上传成功",
"s3_bucket": S3_BUCKET,
"s3_key": s3_key,
"file_size": os.path.getsize(LOCAL_PATH)
})
}
def download_file(url, local_path):
"""用 wget 下载文件到本地"""
cmd = [
"wget",
"-O", local_path, # 输出路径
"--timeout=300", # 超时 5 分钟
"--tries=3", # 最多重试 3 次
"--progress=dot:giga", # 进度显示
url
]
logger.info(f'执行命令: {" ".join(cmd)}')
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=300
)
if result.returncode != 0:
logger.error(f"下载失败: {result.stderr}")
raise RuntimeError(f"wget 下载失败: {result.stderr}")
file_size = os.path.getsize(local_path)
logger.info(f"下载完成: {file_size} bytes")
return file_size
def upload_to_s3(local_path, bucket, key):
"""用 boto3 上传文件到 S3"""
s3 = boto3.client("s3")
s3.upload_file(
Filename=local_path,
Bucket=bucket,
Key=key,
ExtraArgs={
"StorageClass": "STANDARD"
}
)
logger.info(f"上传成功: s3://{bucket}/{key}")
代码拆解
1. 下载部分:subprocess.run + wget
subprocess.run(
cmd,
capture_output=True, # 捕获 stdout 和 stderr
text=True, # 输出转为字符串而非 bytes
timeout=300 # 超时保护
)
关键参数说明:
| wget 参数 | 作用 |
|---|---|
-O /tmp/xxx |
指定保存路径(Lambda 的 /tmp 最多 10GB) |
--timeout=300 |
连接超时 300 秒 |
--tries=3 |
失败自动重试 3 次 |
--progress=dot:giga |
用点号显示进度,适合日志查看 |
2. 上传部分:boto3 upload_file
s3.upload_file(
Filename=local_path, # 本地文件路径
Bucket=bucket, # S3 桶
Key=key, # S3 中的对象路径
ExtraArgs={"StorageClass": "STANDARD"}
)
boto3 的 upload_file 会自动处理大文件分片上传(超过 8MB 自动 multipart),不需要自己写分片逻辑。
Lambda 配置步骤
Step 1:创建 Lambda 函数
运行时选 Python 3.12(或其他 3.x 版本)。
Step 2:设置环境变量
在 Lambda 控制台 -> Configuration -> Environment variables 中设置:
DOWNLOAD_URL:你要下载的文件 URLS3_BUCKET:目标 S3 存储桶名S3_PREFIX:S3 中的文件夹路径(如 downloads)
Step 3:IAM Role 权限
Lambda 执行角色需要 S3 写入权限:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
}
Step 4:超时和内存
- 超时:设成 5-15 分钟,取决于文件大小
- 内存:至少 512MB,大文件建议 1024MB 以上
Step 5:网络
如果 Lambda 在 VPC 私有子网中,需要 NAT Gateway 才能访问外网下载文件。如果 Lambda 不关联 VPC,默认就能访问公网。
超时和内存注意事项
| 限制 | 默认 | 最大 |
|---|---|---|
| 超时 | 3 秒 | 15 分钟 |
| 内存 | 128MB | 10,240MB |
| /tmp 存储 | 512MB | 10 GB |
| 解压后包大小 | 250MB | 250MB |
Lambda 的 /tmp 默认 512MB,最大可以加到 10GB。大文件记得在控制台 Ephemeral storage 里调大。
常见问题
Q1: wget 下载报 403/401 怎么办?
加 Headers:
cmd = [
"wget",
"-O", local_path,
"--header=Authorization: Bearer YOUR_TOKEN",
"--header=Accept: application/octet-stream",
url
]
Q2: 下载的文件名不固定?
用时间戳/UUID 避免冲突:
import time
filename = f"file_{int(time.time())}.zip"
local_path = f"/tmp/{filename}"
Q3: 下载完想删本地文件?
os.remove(local_path)
logger.info("本地临时文件已清理")
Q4: 能不能同时下载多个文件?
用 ThreadPoolExecutor 并行下载:
from concurrent.futures import ThreadPoolExecutor
def download_many(urls):
with ThreadPoolExecutor(max_workers=3) as executor:
executor.map(download_file, urls, ["/tmp/file1", "/tmp/file2", "/tmp/file3"])
Q5: 和直接用 requests 比哪个好?
| 对比项 | wget | requests |
|---|---|---|
| 需要装包? | 不需要 | 需要 |
| 大文件性能 | 更快(C 实现) | 稍慢 |
| 断点续传 | wget -c | 需自己写 |
| 自定义 Headers | 支持 | 支持 |
结论:简单下载场景首选 wget;需要复杂认证/回调逻辑时用 requests。
总结
核心思路就三步:
- wget 下载文件到 /tmp(零依赖,Lambda 自带)
- boto3 上传到 S3(boto3 运行时自带)
- 配置好 IAM Role 和 超时/内存
整个方案不需要 Lambda Layer、不需要第三方依赖,zip 包就一个 Python 文件,部署极简。