基于AWS Chalice与Lambda构建S3图片下载API的部署问题排查
问题描述
我通过部署在Lambda上的AWS Chalice构建图片下载API,图片存储于私有AWS S3桶中。由于需要至少1个月长期可用的下载链接,未使用S3预签名URL。本地运行Chalice时下载的图片可正常打开,但部署到AWS后下载的图片无法打开。
代码实现
@app.route("/img/download", methods=["GET"]) def download_image_api(): def download_img(): bucket = "my-bucket-name" key = "my-key-name" filename = "imgae-file-name" filepath = f"/tmp/{filename}" # download imgae to lambda s3 = boto3.client( resource="s3", region_name=config.AWS_REGION_NAME, aws_access_key_id=config.ACCESS_ID, aws_secret_access_key=config.SECRET_KEY ) s3.download_file(bucket, key, filepath) # build headers file_size = os.path.getsize(filepath) headers = { 'Content-Type': 'application/octet-stream', 'Content-Disposition': f'attachment; filename={filename}', 'Content-Length': str(file_size) } # build body f = open(filepath, 'rb') body = f.read() return Response(body=body, headers=headers)
错误现象
部署到AWS后下载的图片无法打开,文件呈现损坏状态。
解决方案
1. 修复代码语法与逻辑错误
- 删除多余且未调用的空函数
download_img(),调整代码缩进 - 修正
boto3.client()初始化参数:第一个参数是服务名称,不是resource,应直接传"s3" - 修正文件名拼写错误
imgae-file-name为image-file-name - 用
with语句打开文件,避免资源泄漏导致读取不完整
修复后代码:
@app.route("/img/download", methods=["GET"]) def download_image_api(): bucket = "my-bucket-name" key = "my-key-name" filename = "image-file-name" filepath = f"/tmp/{filename}" # 初始化S3客户端 s3 = boto3.client( "s3", region_name=config.AWS_REGION_NAME, aws_access_key_id=config.ACCESS_ID, aws_secret_access_key=config.SECRET_KEY ) s3.download_file(bucket, key, filepath) # 构建响应头 file_size = os.path.getsize(filepath) headers = { 'Content-Type': 'application/octet-stream', 'Content-Disposition': f'attachment; filename={filename}', 'Content-Length': str(file_size) } # 读取文件内容 with open(filepath, 'rb') as f: body = f.read() return Response(body=body, headers=headers)
2. 检查Lambda权限
确保Lambda执行角色拥有目标S3桶的s3:GetObject权限,否则会导致下载的文件为空或损坏。
3. 优化方案:流式返回(无需临时文件)
为避免/tmp目录限制和文件读写问题,建议直接从S3流式读取并返回响应,提升性能同时减少出错概率:
@app.route("/img/download", methods=["GET"]) def download_image_api(): bucket = "my-bucket-name" key = "my-key-name" filename = "image-file-name" s3 = boto3.client( "s3", region_name=config.AWS_REGION_NAME, aws_access_key_id=config.ACCESS_ID, aws_secret_access_key=config.SECRET_KEY ) s3_response = s3.get_object(Bucket=bucket, Key=key) headers = { 'Content-Type': s3_response['ContentType'], # 使用S3存储的原始文件类型更准确 'Content-Disposition': f'attachment; filename={filename}', 'Content-Length': str(s3_response['ContentLength']) } return Response(body=s3_response['Body'].read(), headers=headers)
内容的提问来源于stack exchange,提问作者Eric Wang
相关产品推荐
相关产品推荐

