如何在Amazon S3存储桶中上传同名文件且避免覆盖现有文件
Got it,要解决S3上传同名文件被覆盖的问题,核心就是让每个文件在S3中的Key(也就是文件名)保持唯一。这里有几个实用的方案,你可以根据自己的需求选择:
方案1:给文件名添加时间戳后缀(最易实现)
直接用当前时间的字符串作为文件名后缀,保证每次上传的Key都不一样。比如生成 test_20240520143022.pdf 这样的文件名。
修改后的Lambda代码:
import json import base64 import boto3 from datetime import datetime def lambda_handler(event, context): print("event",event) print("context",context) s3Object = boto3.client("s3") get_file_content = event["content"] decode_content = base64.b64decode(get_file_content) # 生成精确到秒的时间戳后缀 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") # 拼接唯一Key unique_key = f"test_{timestamp}.pdf" s3_upload = s3Object.put_object(Bucket="apigwbucket001", Key=unique_key, Body=decode_content) return { 'statusCode': 200, 'body': json.dumps(f'文件上传成功!S3中的文件名是:{unique_key}') }
优点:无需额外检查S3状态,代码简单,适合对文件名格式要求不严格的场景。
方案2:添加随机字符串后缀(高并发友好)
用UUID生成随机字符串作为后缀,即使同一秒内有多个上传请求,也能保证Key不重复。
修改后的Lambda代码:
import json import base64 import boto3 import uuid def lambda_handler(event, context): print("event",event) print("context",context) s3Object = boto3.client("s3") get_file_content = event["content"] decode_content = base64.b64decode(get_file_content) # 生成8位随机字符串(UUID截取前8位足够唯一) random_str = str(uuid.uuid4())[:8] unique_key = f"test_{random_str}.pdf" s3_upload = s3Object.put_object(Bucket="apigwbucket001", Key=unique_key, Body=decode_content) return { 'statusCode': 200, 'body': json.dumps(f'文件上传成功!S3中的文件名是:{unique_key}') }
优点:几乎不会出现Key冲突,适合高并发的上传场景。
方案3:检查已有文件并递增序号(版本化命名)
如果希望保留原始文件名风格,用序号区分不同版本(比如 test.pdf、test_1.pdf、test_2.pdf),可以先检查S3中已有的同名文件,然后生成递增的序号。
修改后的Lambda代码:
import json import base64 import boto3 import re def lambda_handler(event, context): print("event",event) print("context",context) s3 = boto3.client("s3") bucket_name = "apigwbucket001" base_filename = "test" file_ext = ".pdf" get_file_content = event["content"] decode_content = base64.b64decode(get_file_content) # 列出S3中所有以"test"开头的文件 existing_files = [] response = s3.list_objects_v2(Bucket=bucket_name, Prefix=base_filename) if 'Contents' in response: for obj in response['Contents']: key = obj['Key'] # 匹配原始文件名和带序号的文件名格式 match = re.match(rf"{base_filename}(?:_(\d+))?{file_ext}$", key) if match: existing_files.append(key) # 生成下一个版本的文件名 if not existing_files: unique_key = f"{base_filename}{file_ext}" else: # 提取已有的最大序号 max_version = 0 for file in existing_files: if "_" in file: version_num = int(file.split("_")[-1].replace(file_ext, "")) if version_num > max_version: max_version = version_num unique_key = f"{base_filename}_{max_version + 1}{file_ext}" # 上传文件 s3.put_object(Bucket=bucket_name, Key=unique_key, Body=decode_content) return { 'statusCode': 200, 'body': json.dumps(f'文件上传成功!S3中的文件名是:{unique_key}') }
注意:如果有多个Lambda函数同时处理上传请求,可能会出现竞争条件(比如两个请求同时生成相同的序号)。如果要避免这个问题,可以结合DynamoDB来记录当前最大版本号,保证序号生成的原子性。
额外优化:让前端传递原始文件名
如果你的Postman请求可以传递原始文件名(比如在请求体里加filename字段),可以让Lambda根据用户提供的文件名来生成唯一Key,灵活性更高。比如:
# 假设event中包含filename字段 original_filename = event.get("filename", "test.pdf") name, ext = original_filename.rsplit(".", 1) # 然后用上面的时间戳/随机字符串方案生成唯一Key: unique_key = f"{name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{ext}"
内容的提问来源于stack exchange,提问作者Anuj
相关产品推荐
相关产品推荐

