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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 17:14:08