chb2026 commited on
Commit
ac6b83d
·
verified ·
1 Parent(s): facf217

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -109
app.py CHANGED
@@ -15,13 +15,20 @@ app = Flask(__name__)
15
 
16
  # 环境变量配置
17
  HF_TOKEN = os.environ.get("HF_TOKEN")
18
- SPACE_ID = os.environ.get("SPACE_ID")
19
  MAX_SIZE = int(os.environ.get("MAX_SIZE", 10)) * 1024 * 1024 # 转字节
20
  API_KEY = os.environ.get("API_KEY")
21
 
 
 
 
22
  hf_api = HfApi(token=HF_TOKEN)
23
 
 
 
 
24
  def validate_auth():
 
25
  if not API_KEY:
26
  return True
27
  header_key = request.headers.get("X-API-Key")
@@ -29,30 +36,51 @@ def validate_auth():
29
  return header_key == API_KEY or param_key == API_KEY
30
 
31
  def generate_filename(original_filename):
 
32
  unique_id = uuid.uuid4().hex[:8]
33
  timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
34
  _, ext = os.path.splitext(original_filename)
 
 
35
  return f"{timestamp}-{unique_id}{ext}"
36
 
37
  def save_to_space(content, filename):
38
- path = f"images/{filename}"
39
- hf_api.upload_file(
40
- repo_id=SPACE_ID,
41
- path_in_repo=path,
42
- path_or_fileobj=content
43
- )
44
- return f"https://{SPACE_ID.split('/')[-1]}.hf.space/{path}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  @app.route("/", methods=["GET"])
47
  def index():
 
48
  return render_template("index.html")
49
 
50
  @app.route("/images/<path:filename>")
51
  def serve_image(filename):
 
52
  return send_from_directory("images", filename)
53
 
54
  @app.route("/api/1/upload", methods=["GET", "POST"])
55
  def upload():
 
56
  # 鉴权检查
57
  if not validate_auth():
58
  return jsonify({
@@ -61,111 +89,135 @@ def upload():
61
  "status_txt": "Unauthorized"
62
  }), 401
63
 
64
- # 获取文件数据
65
- source = None
66
- if request.method == "POST":
67
- if "source" in request.files:
68
- file = request.files["source"]
69
- source = file.read()
70
- original_filename = file.filename
71
- else:
72
- source = request.form.get("source")
73
- original_filename = "uploaded_file"
74
- else:
75
- source = request.args.get("source")
76
  original_filename = "uploaded_file"
77
 
78
- # 处理Base64、文件或URL
79
- if isinstance(source, bytes):
80
- content = source
81
- elif source and source.startswith(("http://", "https://")):
82
- response = requests.get(source)
83
- content = response.content
84
- original_filename = os.path.basename(source)
85
- elif source and source.startswith("data:"):
86
- header, data = source.split(",", 1)
87
- mime_type = header.split(":")[1].split(";")[0]
88
- content = base64.b64decode(data)
89
- ext = mimetypes.guess_extension(mime_type)
90
- original_filename = f"uploaded_file{ext}"
91
- else:
92
- return jsonify({
93
- "status_code": 400,
94
- "error": {"message": "Invalid source"},
95
- "status_txt": "Bad Request"
96
- }), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- # 验证大小限制
99
- if len(content) > MAX_SIZE:
100
  return jsonify({
101
- "status_code": 413,
102
- "error": {"message": f"File exceeds {MAX_SIZE//1024//1024}MB limit"},
103
- "status_txt": "Payload Too Large"
104
- }), 413
105
-
106
- # 处理图片属性
107
- try:
108
- image = Image.open(BytesIO(content))
109
- width, height = image.size
110
- mime_type = Image.MIME[image.format]
111
- except:
112
- width = height = 0
113
- mime_type = mimetypes.guess_type(original_filename)[0] or "application/octet-stream"
114
-
115
- # 生成存储信息
116
- filename = generate_filename(original_filename)
117
- file_url = save_to_space(BytesIO(content), filename)
118
-
119
- # 构造响应
120
- response_data = {
121
- "status_code": 200,
122
- "success": {"message": "file uploaded", "code": 200},
123
- "image": {
124
- "name": os.path.splitext(filename)[0],
125
- "extension": os.path.splitext(filename)[1][1:],
126
- "size": len(content),
127
- "width": width,
128
- "height": height,
129
- "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
130
- "date_gmt": (datetime.now() + timedelta(hours=3)).strftime("%Y-%m-%d %H:%M:%S"),
131
- "title": request.form.get("title") or os.path.splitext(original_filename)[0],
132
- "description": request.form.get("description"),
133
- "nsfw": int(request.form.get("nsfw", 0)),
134
- "md5": None, # 可以实现MD5计算如果需要
135
- "storage_mode": "space",
136
- "source_md5": None,
137
- "original_filename": original_filename,
138
- "views": 0,
139
- "likes": 0,
140
- "is_animated": int(getattr(image, "is_animated", False)),
141
- "is_360": 0,
142
- "category_id": request.form.get("category_id"),
143
- "id_encoded": uuid.uuid4().hex[:8],
144
- "filename": filename,
145
- "mime": mime_type,
146
- "url": file_url,
147
- "ratio": width / height if height else 0,
148
- "size_formatted": f"{len(content)/1024/1024:.1f} MB",
149
- "url_viewer": file_url,
150
- "url_short": file_url,
151
- "display_url": file_url,
152
- "display_width": width,
153
- "display_height": height,
154
- "views_label": "views",
155
- "likes_label": "likes",
156
- "how_long_ago": "moments ago",
157
- "delete_url": None # 可以实现删除URL如果需要
158
- },
159
- "status_txt": "OK"
160
- }
161
-
162
- response_format = request.args.get("format", "json")
163
- if response_format == "txt":
164
- return file_url
165
- elif response_format == "redirect":
166
- return redirect(file_url, code=302)
167
- else:
168
- return jsonify(response_data)
169
 
170
  if __name__ == "__main__":
171
  app.run(host="0.0.0.0", port=7860, debug=True)
 
15
 
16
  # 环境变量配置
17
  HF_TOKEN = os.environ.get("HF_TOKEN")
18
+ SPACE_ID = os.environ.get("SPACE_ID", "username/spacename") # 格式: username/spacename
19
  MAX_SIZE = int(os.environ.get("MAX_SIZE", 10)) * 1024 * 1024 # 转字节
20
  API_KEY = os.environ.get("API_KEY")
21
 
22
+ # 从 SPACE_ID 构建域名
23
+ SPACE_DOMAIN = f"{SPACE_ID.replace('/', '-')}.hf.space"
24
+
25
  hf_api = HfApi(token=HF_TOKEN)
26
 
27
+ # 确保上传目录存在
28
+ os.makedirs("images", exist_ok=True)
29
+
30
  def validate_auth():
31
+ """验证 API 密钥"""
32
  if not API_KEY:
33
  return True
34
  header_key = request.headers.get("X-API-Key")
 
36
  return header_key == API_KEY or param_key == API_KEY
37
 
38
  def generate_filename(original_filename):
39
+ """生成唯一的文件名"""
40
  unique_id = uuid.uuid4().hex[:8]
41
  timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
42
  _, ext = os.path.splitext(original_filename)
43
+ if not ext:
44
+ ext = ".jpg" # 默认扩展名
45
  return f"{timestamp}-{unique_id}{ext}"
46
 
47
  def save_to_space(content, filename):
48
+ """保存文件到 Space"""
49
+ # 先保存到本地
50
+ local_path = os.path.join("images", filename)
51
+ with open(local_path, "wb") as f:
52
+ f.write(content)
53
+
54
+ # 上传到 Space
55
+ try:
56
+ hf_api.upload_file(
57
+ repo_id=SPACE_ID,
58
+ path_in_repo=f"images/{filename}",
59
+ path_or_fileobj=local_path
60
+ )
61
+ # 返回可访问的 URL
62
+ return f"https://{SPACE_DOMAIN}/images/{filename}"
63
+ except Exception as e:
64
+ print(f"Upload error: {e}")
65
+ return None
66
+ finally:
67
+ # 清理本地文件
68
+ if os.path.exists(local_path):
69
+ os.remove(local_path)
70
 
71
  @app.route("/", methods=["GET"])
72
  def index():
73
+ """渲染上传页面"""
74
  return render_template("index.html")
75
 
76
  @app.route("/images/<path:filename>")
77
  def serve_image(filename):
78
+ """提供图片访问服务"""
79
  return send_from_directory("images", filename)
80
 
81
  @app.route("/api/1/upload", methods=["GET", "POST"])
82
  def upload():
83
+ """处理上传请求"""
84
  # 鉴权检查
85
  if not validate_auth():
86
  return jsonify({
 
89
  "status_txt": "Unauthorized"
90
  }), 401
91
 
92
+ try:
93
+ # 获取文件数据
94
+ source = None
 
 
 
 
 
 
 
 
 
95
  original_filename = "uploaded_file"
96
 
97
+ if request.method == "POST":
98
+ if "source" in request.files:
99
+ file = request.files["source"]
100
+ source = file.read()
101
+ original_filename = file.filename
102
+ else:
103
+ source = request.form.get("source")
104
+ else:
105
+ source = request.args.get("source")
106
+
107
+ if not source:
108
+ return jsonify({
109
+ "status_code": 400,
110
+ "error": {"message": "No source provided"},
111
+ "status_txt": "Bad Request"
112
+ }), 400
113
+
114
+ # 处理不同类型的输入
115
+ if isinstance(source, bytes):
116
+ content = source
117
+ elif source.startswith(("http://", "https://")):
118
+ response = requests.get(source)
119
+ content = response.content
120
+ original_filename = os.path.basename(source)
121
+ elif source.startswith("data:"):
122
+ try:
123
+ header, data = source.split(",", 1)
124
+ mime_type = header.split(":")[1].split(";")[0]
125
+ content = base64.b64decode(data)
126
+ ext = mimetypes.guess_extension(mime_type)
127
+ original_filename = f"uploaded_file{ext}"
128
+ except Exception as e:
129
+ return jsonify({
130
+ "status_code": 400,
131
+ "error": {"message": "Invalid base64 data"},
132
+ "status_txt": "Bad Request"
133
+ }), 400
134
+ else:
135
+ try:
136
+ content = base64.b64decode(source)
137
+ except Exception as e:
138
+ return jsonify({
139
+ "status_code": 400,
140
+ "error": {"message": "Invalid source format"},
141
+ "status_txt": "Bad Request"
142
+ }), 400
143
+
144
+ # 验证文件大小
145
+ if len(content) > MAX_SIZE:
146
+ return jsonify({
147
+ "status_code": 413,
148
+ "error": {"message": f"File exceeds {MAX_SIZE//1024//1024}MB limit"},
149
+ "status_txt": "Payload Too Large"
150
+ }), 413
151
+
152
+ # 获取图片信息
153
+ try:
154
+ image = Image.open(BytesIO(content))
155
+ width, height = image.size
156
+ mime_type = Image.MIME[image.format]
157
+ except Exception as e:
158
+ width = height = 0
159
+ mime_type = mimetypes.guess_type(original_filename)[0] or "application/octet-stream"
160
+
161
+ # 生成文件名并保存
162
+ filename = generate_filename(original_filename)
163
+ file_url = save_to_space(content, filename)
164
+
165
+ if not file_url:
166
+ return jsonify({
167
+ "status_code": 500,
168
+ "error": {"message": "Failed to save file"},
169
+ "status_txt": "Internal Server Error"
170
+ }), 500
171
+
172
+ # 构造响应数据
173
+ response_data = {
174
+ "status_code": 200,
175
+ "success": {
176
+ "message": "file uploaded",
177
+ "code": 200
178
+ },
179
+ "image": {
180
+ "name": os.path.splitext(filename)[0],
181
+ "extension": os.path.splitext(filename)[1][1:],
182
+ "size": len(content),
183
+ "width": width,
184
+ "height": height,
185
+ "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
186
+ "date_gmt": (datetime.now() + timedelta(hours=3)).strftime("%Y-%m-%d %H:%M:%S"),
187
+ "title": request.form.get("title") or os.path.splitext(original_filename)[0],
188
+ "description": request.form.get("description"),
189
+ "nsfw": int(request.form.get("nsfw", 0)),
190
+ "md5": None,
191
+ "storage_mode": "space",
192
+ "original_filename": original_filename,
193
+ "views": 0,
194
+ "likes": 0,
195
+ "is_animated": int(getattr(image, "is_animated", False)),
196
+ "filename": filename,
197
+ "mime": mime_type,
198
+ "url": file_url,
199
+ "size_formatted": f"{len(content)/1024/1024:.1f} MB",
200
+ "display_url": file_url,
201
+ "url_viewer": file_url,
202
+ },
203
+ "status_txt": "OK"
204
+ }
205
+
206
+ # 根据请求的格式返回响应
207
+ response_format = request.args.get("format", "json")
208
+ if response_format == "txt":
209
+ return file_url
210
+ elif response_format == "redirect":
211
+ return redirect(file_url, code=302)
212
+ else:
213
+ return jsonify(response_data)
214
 
215
+ except Exception as e:
 
216
  return jsonify({
217
+ "status_code": 500,
218
+ "error": {"message": str(e)},
219
+ "status_txt": "Internal Server Error"
220
+ }), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
  if __name__ == "__main__":
223
  app.run(host="0.0.0.0", port=7860, debug=True)