gallyga commited on
Commit
95e351c
·
verified ·
1 Parent(s): 6dae108

Update src/image_uploader.py

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