更新
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from qcloud_cos import CosConfig
|
||||
from qcloud_cos import CosS3Client
|
||||
from qcloud_cos.cos_exception import CosServiceError
|
||||
|
||||
# 配置日志输出格式(同时输出到屏幕和文件)
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler("cos_upload.log", mode='a'), # 追加模式保存到日志文件
|
||||
logging.StreamHandler(sys.stdout) # 在终端屏幕显示
|
||||
]
|
||||
)
|
||||
|
||||
# ==========================================
|
||||
# 必填:请在此处替换为您自己的 COS 配置信息
|
||||
# ==========================================
|
||||
SECRET_ID = 'AKIDZ0EWnsmWnwvmT0KRLFC4mNlyzufDYRhm' # 替换为您的 SecretId
|
||||
SECRET_KEY = 'yjJiuseHhuCLK8mcVVbaRJAwfHrKya0C' # 替换为您的 SecretKey
|
||||
REGION = 'ap-guangzhou' # 替换为您存储桶所在的地域,例如 ap-guangzhou
|
||||
BUCKET = 'gz-1349751149' # 替换为您的 Bucket 名称,包含 appid
|
||||
# ==========================================
|
||||
|
||||
# 选填:高级配置
|
||||
UPLOAD_DIR = '.' # 要上传的本地目录,'.' 代表当前目录
|
||||
COS_PREFIX = 'uploads/images/20260513/data/' # 上传到 COS 时的远程路径前缀(如 'my_folder/'),留空则存放在根目录
|
||||
IGNORE_FILES = ['.git', '.env', '__pycache__'] # 忽略的文件或目录片段
|
||||
|
||||
def init_cos_client():
|
||||
"""初始化 COS 客户端"""
|
||||
# 针对超大文件,增加 Timeout 超时时间(单位:秒),防止网络波动导致断开
|
||||
config = CosConfig(Region=REGION, SecretId=SECRET_ID, SecretKey=SECRET_KEY, Timeout=300)
|
||||
return CosS3Client(config)
|
||||
|
||||
def should_ignore(file_path):
|
||||
"""判断是否需要忽略该文件"""
|
||||
# 忽略脚本自身
|
||||
if os.path.basename(file_path) == os.path.basename(__file__):
|
||||
return True
|
||||
|
||||
# 忽略指定的隐藏文件或缓存目录
|
||||
for ignore_item in IGNORE_FILES:
|
||||
if ignore_item in file_path:
|
||||
return True
|
||||
return False
|
||||
|
||||
class UploadProgress:
|
||||
def __init__(self, filename):
|
||||
self.filename = os.path.basename(filename)
|
||||
self.reported_percents = set()
|
||||
|
||||
def __call__(self, consumed_bytes, total_bytes):
|
||||
if total_bytes:
|
||||
rate = int(100 * (float(consumed_bytes) / float(total_bytes)))
|
||||
# 每隔 10% 打印一次日志,避免日志过多刷屏
|
||||
if rate % 10 == 0 and rate not in self.reported_percents:
|
||||
self.reported_percents.add(rate)
|
||||
logging.info("[PROGRESS] %s 进度: %d%% (%d/%d 字节)", self.filename, rate, consumed_bytes, total_bytes)
|
||||
|
||||
def upload_files(client):
|
||||
"""遍历目录并上传文件"""
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
|
||||
# 遍历本地目录
|
||||
for root, dirs, files in os.walk(UPLOAD_DIR):
|
||||
for file in files:
|
||||
local_file_path = os.path.join(root, file)
|
||||
|
||||
# 过滤不需要上传的文件
|
||||
if should_ignore(local_file_path):
|
||||
continue
|
||||
|
||||
# 计算在 COS 上的对象键 (Object Key),即远程相对路径
|
||||
rel_path = os.path.relpath(local_file_path, UPLOAD_DIR)
|
||||
|
||||
# 兼容 Windows 路径:将反斜杠转为正斜杠
|
||||
rel_path_unix = rel_path.replace('\\', '/')
|
||||
|
||||
# 拼接 COS 远程前缀
|
||||
if COS_PREFIX:
|
||||
cos_key = "{0}/{1}".format(COS_PREFIX.rstrip('/'), rel_path_unix)
|
||||
else:
|
||||
cos_key = rel_path_unix
|
||||
|
||||
try:
|
||||
# ==========================================
|
||||
# 不重复上传逻辑:检查远端是否存在且大小一致
|
||||
# ==========================================
|
||||
local_size = os.path.getsize(local_file_path)
|
||||
try:
|
||||
# 获取 COS 上的文件信息
|
||||
head = client.head_object(Bucket=BUCKET, Key=cos_key)
|
||||
remote_size = int(head.get('Content-Length', 0))
|
||||
|
||||
# 如果远端文件存在且大小一致,则认为已上传过,直接跳过
|
||||
if remote_size == local_size:
|
||||
logging.info("[SKIP] 远端已存在且大小一致,跳过上传: {0}".format(cos_key))
|
||||
success_count += 1
|
||||
continue
|
||||
except CosServiceError as e:
|
||||
# 404 说明远端文件不存在,可以正常开始上传;其他错误则抛出
|
||||
if e.get_status_code() != 404:
|
||||
raise e
|
||||
|
||||
logging.info("[START] 正在上传: {0} -> cos://{1}/{2}".format(local_file_path, BUCKET, cos_key))
|
||||
|
||||
response = client.upload_file(
|
||||
Bucket=BUCKET,
|
||||
LocalFilePath=local_file_path,
|
||||
Key=cos_key,
|
||||
# 80G超大文件优化:增加单个分块大小,减小并发防止带宽被占满而导致超时
|
||||
PartSize=100, # 分块大小改为 100 MB
|
||||
MAXThread=3, # 并发线程数降低至 3,求稳
|
||||
EnableMD5=False, # 是否校验 MD5
|
||||
progress_callback=UploadProgress(local_file_path) # 添加进度回调
|
||||
)
|
||||
logging.info("[SUCCESS] 上传成功: {0}".format(cos_key))
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
logging.error("[ERROR] 上传失败: {0}, 错误信息: {1}".format(local_file_path, e))
|
||||
fail_count += 1
|
||||
|
||||
logging.info("[DONE] 全部上传任务执行完毕!成功: {0} 个文件,失败: {1} 个文件。".format(success_count, fail_count))
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 简单拦截,防止未修改配置直接运行
|
||||
if SECRET_ID == 'YOUR_SECRET_ID':
|
||||
logging.error("运行失败:请先在脚本代码中配置您的 SECRET_ID, SECRET_KEY, REGION 和 BUCKET 信息。")
|
||||
sys.exit(1)
|
||||
|
||||
logging.info("初始化 COS 客户端...")
|
||||
client = init_cos_client()
|
||||
|
||||
logging.info("开始扫描当前目录并准备上传...")
|
||||
upload_files(client)
|
||||
Reference in New Issue
Block a user