fix(api): image_get 500 error for non-ASCII filename uploads

- Fixes 500 error when uploading images with non-ASCII filenames via /api/image_get - Improves file path handling to support both development and Docker environments - Adds exception handling to gracefully fall back to Docker path logic if path correction fails - Ensures robust error handling so path issues do not crash the entire endpoint

keyboardstaff committed Mar 26, 2026 at 01:19 UTC 1160195fb5994a182689ffbc5f1610f407a74dd9
1 file changed +27 -19
api/image_get.py
+27 -19
@@ -1,5 +1,6 @@
1 import base64
2 import os
3 +from urllib.parse import quote
4 from helpers.api import ApiHandler, Request, Response, send_file
5 from helpers import files, runtime
6 import io
@@ -47,24 +48,31 @@ class ImageGet(ApiHandler):
48
49 # in development environment, try to serve the image from local file system if exists, otherwise from docker
50 if runtime.is_development():
50 - if files.exists(path):
51 - response = send_file(path)
52 - elif await runtime.call_development_function(files.exists, path):
53 - b64_content = await runtime.call_development_function(
54 - files.read_file_base64, path
55 - )
56 - file_content = base64.b64decode(b64_content)
57 - mime_type, _ = guess_type(filename)
58 - if not mime_type:
59 - mime_type = "application/octet-stream"
60 - response = send_file(
61 - io.BytesIO(file_content),
62 - mimetype=mime_type,
63 - as_attachment=False,
64 - download_name=filename,
65 - )
51 + # Convert /a0/... Docker paths to local absolute paths
52 + local_path = files.fix_dev_path(path)
53 + if files.exists(local_path):
54 + response = send_file(local_path)
55 else:
67 - response = _send_fallback_icon("image")
56 + # Try fetching from Docker via RFC as fallback
57 + try:
58 + if await runtime.call_development_function(files.exists, path):
59 + b64_content = await runtime.call_development_function(
60 + files.read_file_base64, path
61 + )
62 + file_content = base64.b64decode(b64_content)
63 + mime_type, _ = guess_type(filename)
64 + if not mime_type:
65 + mime_type = "application/octet-stream"
66 + response = send_file(
67 + io.BytesIO(file_content),
68 + mimetype=mime_type,
69 + as_attachment=False,
70 + download_name=filename,
71 + )
72 + else:
73 + response = _send_fallback_icon("image")
74 + except Exception:
75 + response = _send_fallback_icon("image")
76 else:
77 if files.exists(path):
78 response = send_file(path)
@@ -74,7 +82,7 @@ class ImageGet(ApiHandler):
82 # Add cache headers for better device sync performance
83 response.headers["Cache-Control"] = "public, max-age=3600"
84 response.headers["X-File-Type"] = "image"
77 - response.headers["X-File-Name"] = filename
85 + response.headers["X-File-Name"] = quote(filename)
86 return response
87 else:
88 # Handle non-image files with fallback icons
@@ -135,7 +143,7 @@ def _send_file_type_icon(file_ext, filename=None):
143 response.headers["X-File-Type"] = "icon"
144 response.headers["X-Icon-Type"] = icon_name
145 if filename:
138 - response.headers["X-File-Name"] = filename
146 + response.headers["X-File-Name"] = quote(filename)
147
148 return response
149