求助:AWS Lambda实现S3间JSON转CSV上传遇只读文件系统错误
问题分析与解决方案
首先,你碰到的[Errno 30] Read-only file system错误是AWS Lambda运行环境的典型限制导致的:Lambda的默认工作目录是只读的,只有/tmp目录是可读写的,你直接尝试创建data.csv会因为没有写入权限而失败。
除此之外,你的代码还有几个小问题需要修正:
- 变量
data未定义,应该用你已经声明的json_data s3对象没有初始化,直接调用s3.Object会报错- 两次打开文件可以优化,甚至可以直接在内存中处理CSV,避免磁盘IO
修正后的代码方案
方案1:使用/tmp目录写入临时文件
这是最直接的修改,把文件路径替换为/tmp/data.csv,同时修复其他问题:
import boto3 import botocore import csv def lambda_handler(event, context): # 初始化S3资源 s3 = boto3.resource('s3') BUCKET_NAME = 'name of the bucket' # 替换为你的目标桶名称 KEY = 'OUTPUT.csv' # 替换为目标对象的key json_data = [{"id":"1","name":"test"},{"id":"2","name":"good"}] # 使用/tmp目录存储临时CSV文件 csv_file_path = '/tmp/data.csv' with open(csv_file_path, "w") as file: csv_writer = csv.writer(file) csv_writer.writerow(['id', 'name']) # 修复变量名错误:data -> json_data for item in json_data: csv_writer.writerow([item.get('id'), item.get('name')]) # 读取生成的CSV文件并上传到S3 try: obj = s3.Object(BUCKET_NAME, KEY) with open(csv_file_path, 'rb') as file: obj.put(Body=file.read()) except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == "404": print("目标存储桶不存在或无法访问。") else: raise # 生成预签名URL s3client = boto3.client('s3') try: download_url = s3client.generate_presigned_url( 'get_object', Params={ 'Bucket': BUCKET_NAME, 'Key': KEY }, ExpiresIn=3600 ) return {"csv_link": download_url} except Exception as e: # 若utils_exception是自定义模块,可保留;否则直接抛出错误即可 raise e
方案2:使用内存流(更高效)
如果你的JSON数据不大,完全可以不用写入磁盘,直接用io.StringIO在内存中生成CSV,这样更快也避免了磁盘操作:
import boto3 import botocore import csv from io import StringIO def lambda_handler(event, context): s3 = boto3.resource('s3') BUCKET_NAME = 'name of the bucket' KEY = 'OUTPUT.csv' json_data = [{"id":"1","name":"test"},{"id":"2","name":"good"}] # 使用内存流生成CSV csv_buffer = StringIO() csv_writer = csv.writer(csv_buffer) csv_writer.writerow(['id', 'name']) for item in json_data: csv_writer.writerow([item.get('id'), item.get('name')]) # 上传内存中的CSV内容到S3(注意转成字节流) try: obj = s3.Object(BUCKET_NAME, KEY) obj.put(Body=csv_buffer.getvalue().encode('utf-8')) except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == "404": print("目标存储桶不存在或无法访问。") else: raise s3client = boto3.client('s3') try: download_url = s3client.generate_presigned_url( 'get_object', Params={ 'Bucket': BUCKET_NAME, 'Key': KEY }, ExpiresIn=3600 ) return {"csv_link": download_url} except Exception as e: raise e
关键修改点说明
- 使用
/tmp目录:Lambda的/tmp目录是唯一可读写的临时存储,最大容量为512MB,适合存储临时文件 - 变量名修正:将未定义的
data替换为你实际的数据源json_data - 初始化S3资源:在调用
s3.Object前必须先通过boto3.resource('s3')创建S3资源对象 - 内存流优化:避免磁盘IO,提升函数执行效率,尤其适合小数据量的转换
内容的提问来源于stack exchange,提问作者pritesh singh
相关产品推荐
相关产品推荐

