Ai_GirlFriend/开发/0-礼物和服装图片上传到存储桶.py

278 lines
9.1 KiB
Python
Raw Normal View History

2026-02-05 10:06:07 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
图片迁移到新 OSS 存储桶
集成配置和上传功能
"""
import os
import re
import oss2
from pathlib import Path
# 本地图片目录
GIFT_DIR = 'public/images/gifts'
OUTFIT_DIR = 'public/images/outfits'
# OSS 上传路径(保持与数据库一致)
OSS_GIFT_PREFIX = 'uploads/gifts/'
OSS_OUTFIT_PREFIX = 'uploads/outfit/'
class OSSMigrator:
def __init__(self):
self.config = {}
self.bucket = None
def get_input(self, prompt, default=None):
"""获取用户输入"""
if default:
prompt = f"{prompt} [{default}]: "
else:
prompt = f"{prompt}: "
value = input(prompt).strip()
return value if value else default
def validate_endpoint(self, endpoint):
"""验证 Endpoint 格式"""
endpoint = endpoint.replace('https://', '').replace('http://', '')
return endpoint
def configure(self):
"""配置新 OSS 信息"""
print("=" * 60)
print("配置新的 OSS 存储桶")
print("=" * 60)
print("\n请输入新的 OSS 配置信息:\n")
# 获取配置信息
self.config['access_key_id'] = self.get_input("Access Key ID")
if not self.config['access_key_id']:
print("✗ Access Key ID 不能为空")
return False
self.config['access_key_secret'] = self.get_input("Access Key Secret")
if not self.config['access_key_secret']:
print("✗ Access Key Secret 不能为空")
return False
self.config['bucket_name'] = self.get_input("Bucket 名称")
if not self.config['bucket_name']:
print("✗ Bucket 名称不能为空")
return False
self.config['endpoint'] = self.get_input("Endpoint", "oss-cn-hangzhou.aliyuncs.com")
self.config['endpoint'] = self.validate_endpoint(self.config['endpoint'])
# 显示配置摘要
print("\n" + "=" * 60)
print("配置摘要")
print("=" * 60)
print(f"Access Key ID: {self.config['access_key_id'][:8]}...")
print(f"Access Key Secret: {self.config['access_key_secret'][:8]}...")
print(f"Bucket 名称: {self.config['bucket_name']}")
print(f"Endpoint: {self.config['endpoint']}")
print(f"CDN 域名: https://{self.config['bucket_name']}.{self.config['endpoint']}")
# 确认
confirm = self.get_input("\n确认配置?(y/n)", "y")
if confirm.lower() != 'y':
print("已取消")
return False
return True
def init_oss_bucket(self):
"""初始化 OSS 连接"""
try:
auth = oss2.Auth(self.config['access_key_id'], self.config['access_key_secret'])
self.bucket = oss2.Bucket(auth, self.config['endpoint'], self.config['bucket_name'])
return True
except Exception as e:
print(f"✗ 初始化 OSS 失败: {e}")
return False
def test_oss_connection(self):
"""测试 OSS 连接"""
try:
for obj in oss2.ObjectIterator(self.bucket, max_keys=1):
pass
return True
except Exception as e:
print(f"✗ OSS 连接失败: {e}")
return False
def upload_file(self, local_path, oss_path):
"""上传单个文件到 OSS"""
try:
result = self.bucket.put_object_from_file(oss_path, local_path)
if result.status == 200:
return True
else:
print(f" ✗ 上传失败 (状态码: {result.status})")
return False
except Exception as e:
print(f" ✗ 上传错误: {e}")
return False
def upload_gifts(self):
"""上传礼物图片"""
print(f"\n📦 上传礼物图片")
print(f"本地目录: {GIFT_DIR}")
print(f"OSS 路径: {OSS_GIFT_PREFIX}")
if not os.path.exists(GIFT_DIR):
print(f"✗ 目录不存在: {GIFT_DIR}")
return
files = [f for f in os.listdir(GIFT_DIR) if f.endswith(('.png', '.jpg', '.jpeg', '.gif'))]
print(f"找到 {len(files)} 个文件")
success = 0
failed = 0
skipped = 0
for filename in files:
local_path = os.path.join(GIFT_DIR, filename)
oss_path = OSS_GIFT_PREFIX + filename
# 检查文件是否已存在
try:
self.bucket.head_object(oss_path)
print(f" ○ 已存在: {filename}")
skipped += 1
continue
except oss2.exceptions.NoSuchKey:
pass
print(f" ↑ 上传: {filename}")
if self.upload_file(local_path, oss_path):
success += 1
print(f" ✓ 成功")
else:
failed += 1
print(f"\n礼物上传完成: 成功 {success}, 失败 {failed}, 跳过 {skipped}")
def upload_outfits(self):
"""上传服装图片"""
print(f"\n👔 上传服装图片")
print(f"本地目录: {OUTFIT_DIR}")
print(f"OSS 路径: {OSS_OUTFIT_PREFIX}")
if not os.path.exists(OUTFIT_DIR):
print(f"✗ 目录不存在: {OUTFIT_DIR}")
return
files = [f for f in os.listdir(OUTFIT_DIR) if f.endswith(('.png', '.jpg', '.jpeg', '.gif'))]
print(f"找到 {len(files)} 个文件")
success = 0
failed = 0
skipped = 0
# 服装图片需要按类型分类上传
outfit_types = {
'top': ['tshirt', 'shirt', 'sleeve', 'strap', 'shoulder', 'crop', 'cardigan', 'jacket', 'sweatshirt'],
'bottom': ['jeans', 'skirt', 'shorts', 'pants', 'sweatpants'],
'dress': ['dress', 'uniform', 'hanfu', 'lolita']
}
for filename in files:
local_path = os.path.join(OUTFIT_DIR, filename)
# 判断服装类型
outfit_type = 'dress'
for type_name, keywords in outfit_types.items():
if any(keyword in filename.lower() for keyword in keywords):
outfit_type = type_name
break
oss_path = f"{OSS_OUTFIT_PREFIX}{outfit_type}/{filename}"
# 检查文件是否已存在
try:
self.bucket.head_object(oss_path)
print(f" ○ 已存在: {filename} ({outfit_type})")
skipped += 1
continue
except oss2.exceptions.NoSuchKey:
pass
print(f" ↑ 上传: {filename} -> {outfit_type}/")
if self.upload_file(local_path, oss_path):
success += 1
print(f" ✓ 成功")
else:
failed += 1
print(f"\n服装上传完成: 成功 {success}, 失败 {failed}, 跳过 {skipped}")
def generate_env_config(self):
"""生成 .env 配置内容"""
endpoint_with_https = f"https://{self.config['endpoint']}"
cdn_domain = f"https://{self.config['bucket_name']}.{self.config['endpoint']}"
env_content = f"""
# ===== 新的 OSS 配置 =====
ALIYUN_OSS_ACCESS_KEY_ID={self.config['access_key_id']}
ALIYUN_OSS_ACCESS_KEY_SECRET={self.config['access_key_secret']}
ALIYUN_OSS_BUCKET_NAME={self.config['bucket_name']}
ALIYUN_OSS_ENDPOINT={endpoint_with_https}
ALIYUN_OSS_CDN_DOMAIN={cdn_domain}
"""
return env_content
def run(self):
"""运行迁移流程"""
print("=" * 60)
print("图片迁移到新 OSS 存储桶")
print("=" * 60)
# 步骤1: 配置
if not self.configure():
return
# 步骤2: 初始化 OSS
print("\n正在连接 OSS...")
if not self.init_oss_bucket():
return
# 步骤3: 测试连接
if not self.test_oss_connection():
print("\n✗ 无法连接到 OSS请检查配置")
return
print("✓ OSS 连接成功")
# 步骤4: 上传礼物图片
self.upload_gifts()
# 步骤5: 上传服装图片
self.upload_outfits()
# 步骤6: 显示后续操作
print("\n" + "=" * 60)
print("✓ 上传完成!")
print("=" * 60)
print("\n下一步操作:")
print("\n1. 将以下内容添加到 .env 文件:")
print(self.generate_env_config())
print("\n2. 重启服务:")
print(" 开发\\1-启动项目.bat")
print("\n数据库无需修改,图片路径保持不变!")
def main():
try:
migrator = OSSMigrator()
migrator.run()
except KeyboardInterrupt:
print("\n\n用户中断操作")
except Exception as e:
print(f"\n✗ 发生错误: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
main()