gallyga commited on
Commit
2bff1fb
·
verified ·
1 Parent(s): a36a832

Upload image_uploader.py

Browse files
Files changed (1) hide show
  1. src/image_uploader.py +242 -0
src/image_uploader.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import aiohttp
4
+ import logging
5
+ import json
6
+ import aiofiles
7
+ import base64
8
+ from urllib.parse import urlparse
9
+ from .config import Config
10
+
11
+ # 初始化日志
12
+ logger = logging.getLogger("sora-api.image_uploader")
13
+
14
+ class PicGoUploader:
15
+ """PicGo图床上传器"""
16
+
17
+ def __init__(self, picgo_url=None, api_key=None):
18
+ """
19
+ 初始化PicGo上传器
20
+
21
+ Args:
22
+ picgo_url: PicGo服务器URL,默认为 http://127.0.0.1:36677
23
+ api_key: PicGo API密钥(如果启用了)
24
+ """
25
+ self.picgo_url = picgo_url or os.getenv("PICGO_URL", "http://127.0.0.1:36677")
26
+ self.api_key = api_key or os.getenv("PICGO_API_KEY", "")
27
+ self.enabled = os.getenv("ENABLE_PICGO", "").lower() in ("true", "1", "yes")
28
+
29
+ # 日志输出
30
+ if self.enabled:
31
+ logger.info(f"PicGo上传已启用,服务器地址: {self.picgo_url}")
32
+ else:
33
+ logger.info("PicGo上传未启用")
34
+
35
+ async def upload(self, file_path):
36
+ """
37
+ 上传图片到PicGo
38
+
39
+ Args:
40
+ file_path: 图片文件路径
41
+
42
+ Returns:
43
+ 成功: 图片URL
44
+ 失败: None
45
+ """
46
+ if not self.enabled:
47
+ logger.debug("PicGo上传未启用,跳过上传")
48
+ return None
49
+
50
+ if not os.path.exists(file_path):
51
+ logger.error(f"文件不存在: {file_path}")
52
+ return None
53
+
54
+ try:
55
+ # 读取图片文件
56
+ async with aiofiles.open(file_path, "rb") as f:
57
+ file_content = await f.read()
58
+
59
+ # 编码为Base64
60
+ base64_content = base64.b64encode(file_content).decode('utf-8')
61
+
62
+ # 准备上传数据
63
+ data = {
64
+ "list": [{
65
+ "data": f"data:image/{os.path.splitext(file_path)[1][1:]};base64,{base64_content}"
66
+ }]
67
+ }
68
+
69
+ # 准备请求头
70
+ headers = {"Content-Type": "application/json"}
71
+ if self.api_key:
72
+ headers["picgo-api-key"] = self.api_key
73
+
74
+ # 发送请求
75
+ timeout = aiohttp.ClientTimeout(total=60)
76
+ async with aiohttp.ClientSession(timeout=timeout) as session:
77
+ async with session.post(
78
+ f"{self.picgo_url}/upload",
79
+ headers=headers,
80
+ json=data
81
+ ) as response:
82
+ result = await response.json()
83
+
84
+ # 处理响应
85
+ if result.get("success"):
86
+ image_url = result.get("result")[0]
87
+ logger.info(f"图片已成功上传到PicGo: {image_url}")
88
+ return image_url
89
+ else:
90
+ logger.error(f"PicGo上传失败: {result.get('message', '未知错误')}")
91
+ return None
92
+
93
+ except Exception as e:
94
+ logger.error(f"PicGo上传过程出错: {str(e)}")
95
+ return None
96
+
97
+ class WebDAVUploader:
98
+ """WebDAV上传器"""
99
+
100
+ def __init__(self, webdav_url=None, username=None, password=None):
101
+ """
102
+ 初始化WebDAV上传器
103
+
104
+ Args:
105
+ webdav_url: WebDAV服务器URL
106
+ username: WebDAV用户名
107
+ password: WebDAV密码
108
+ """
109
+ self.webdav_url = webdav_url or os.getenv("WEBDAV_URL", "")
110
+ self.username = username or os.getenv("WEBDAV_USERNAME", "")
111
+ self.password = password or os.getenv("WEBDAV_PASSWORD", "")
112
+ self.base_path = os.getenv("WEBDAV_BASE_PATH", "/sora-images")
113
+ self.enabled = os.getenv("ENABLE_WEBDAV", "").lower() in ("true", "1", "yes")
114
+ self.public_url = os.getenv("WEBDAV_PUBLIC_URL", "")
115
+
116
+ # 确保base_path以/开头
117
+ if self.base_path and not self.base_path.startswith('/'):
118
+ self.base_path = f"/{self.base_path}"
119
+
120
+ # 日志输出
121
+ if self.enabled:
122
+ if not self.webdav_url:
123
+ logger.warning("WebDAV上传已启用,但未设置WebDAV URL")
124
+ self.enabled = False
125
+ else:
126
+ logger.info(f"WebDAV上传已启用,服务器地址: {self.webdav_url}")
127
+ else:
128
+ logger.info("WebDAV上传未启用")
129
+
130
+ async def upload(self, file_path):
131
+ """
132
+ 上传图片到WebDAV服务器
133
+
134
+ Args:
135
+ file_path: 图片文件路径
136
+
137
+ Returns:
138
+ 成功: 图片URL
139
+ 失败: None
140
+ """
141
+ if not self.enabled:
142
+ logger.debug("WebDAV上传未启用,跳过上传")
143
+ return None
144
+
145
+ if not os.path.exists(file_path):
146
+ logger.error(f"文件不存在: {file_path}")
147
+ return None
148
+
149
+ try:
150
+ # 读取图片文件
151
+ async with aiofiles.open(file_path, "rb") as f:
152
+ file_content = await f.read()
153
+
154
+ # 准备上传路径
155
+ filename = os.path.basename(file_path)
156
+ remote_path = f"{self.base_path}/{filename}"
157
+
158
+ # 确保目录存在
159
+ await self._ensure_directory(self.base_path)
160
+
161
+ # 上传文件
162
+ timeout = aiohttp.ClientTimeout(total=60)
163
+ auth = aiohttp.BasicAuth(self.username, self.password)
164
+ async with aiohttp.ClientSession(timeout=timeout, auth=auth) as session:
165
+ async with session.put(
166
+ f"{self.webdav_url}{remote_path}",
167
+ data=file_content
168
+ ) as response:
169
+ if response.status in (200, 201, 204):
170
+ # 生成访问URL
171
+ if self.public_url:
172
+ url = f"{self.public_url.rstrip('/')}{remote_path}"
173
+ else:
174
+ url = f"{self.webdav_url}{remote_path}"
175
+
176
+ logger.info(f"图片已成功上传到WebDAV: {url}")
177
+ return url
178
+ else:
179
+ logger.error(f"WebDAV上传失败,状态码: {response.status}")
180
+ return None
181
+
182
+ except Exception as e:
183
+ logger.error(f"WebDAV上传过程出错: {str(e)}")
184
+ return None
185
+
186
+ async def _ensure_directory(self, directory):
187
+ """确保WebDAV目录存在"""
188
+ try:
189
+ timeout = aiohttp.ClientTimeout(total=30)
190
+ auth = aiohttp.BasicAuth(self.username, self.password)
191
+ async with aiohttp.ClientSession(timeout=timeout, auth=auth) as session:
192
+ # 检查目录是否存在
193
+ async with session.request(
194
+ "PROPFIND",
195
+ f"{self.webdav_url}{directory}",
196
+ headers={"Depth": "0"},
197
+ ) as response:
198
+ if response.status == 207:
199
+ return True
200
+
201
+ # 目录不存在,创建目录
202
+ async with session.request(
203
+ "MKCOL",
204
+ f"{self.webdav_url}{directory}"
205
+ ) as response:
206
+ if response.status in (201, 405): # 405表示目录已存在
207
+ return True
208
+ else:
209
+ logger.error(f"创建WebDAV目录失败: {directory}, 状态码: {response.status}")
210
+ return False
211
+ except Exception as e:
212
+ logger.error(f"确保WebDAV目录存在时出错: {str(e)}")
213
+ return False
214
+
215
+ # 创建上传器实例
216
+ picgo_uploader = PicGoUploader()
217
+ webdav_uploader = WebDAVUploader()
218
+
219
+ async def upload_image(file_path):
220
+ """
221
+ 上传图片到配置的服务
222
+
223
+ Args:
224
+ file_path: 图片文件路径
225
+
226
+ Returns:
227
+ 元组 (成功标志, 图片URL或None)
228
+ """
229
+ # 尝试PicGo上传
230
+ if picgo_uploader.enabled:
231
+ picgo_url = await picgo_uploader.upload(file_path)
232
+ if picgo_url:
233
+ return True, picgo_url
234
+
235
+ # 尝试WebDAV上传
236
+ if webdav_uploader.enabled:
237
+ webdav_url = await webdav_uploader.upload(file_path)
238
+ if webdav_url:
239
+ return True, webdav_url
240
+
241
+ # 都上传失败
242
+ return False, None