Store photos on server, save path in Excel instead of base64
- Add POST /upload endpoint to server.py (saves to photos/ dir) - Add DELETE /photos/ endpoint for photo removal - Replace base64 photoData with server-stored photoPath - Export/import uses path string instead of large base64 data
This commit is contained in:
77
server.py
77
server.py
@@ -1,8 +1,83 @@
|
||||
import http.server
|
||||
import socketserver
|
||||
import os
|
||||
import urllib.parse
|
||||
import uuid
|
||||
|
||||
PORT = 8000
|
||||
Handler = http.server.SimpleHTTPRequestHandler
|
||||
PHOTO_DIR = 'photos'
|
||||
os.makedirs(PHOTO_DIR, exist_ok=True)
|
||||
|
||||
|
||||
class Handler(http.server.SimpleHTTPRequestHandler):
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == '/upload':
|
||||
content_len = int(self.headers.get('Content-Length', 0))
|
||||
body = self.rfile.read(content_len)
|
||||
# parse multipart/form-data
|
||||
boundary = self.headers.get('Content-Type', '').split('boundary=')[-1].encode()
|
||||
if not boundary:
|
||||
self.send_error(400, 'Missing boundary')
|
||||
return
|
||||
parts = body.split(b'--' + boundary)
|
||||
filename = None
|
||||
file_data = None
|
||||
for part in parts:
|
||||
if b'Content-Disposition' in part:
|
||||
header_end = part.find(b'\r\n\r\n')
|
||||
if header_end == -1:
|
||||
continue
|
||||
header_section = part[:header_end].decode('utf-8', errors='replace')
|
||||
data = part[header_end + 4:]
|
||||
data = data.rstrip(b'\r\n--')
|
||||
# Check if this part has a filename
|
||||
for line in header_section.split('\r\n'):
|
||||
if 'filename="' in line:
|
||||
orig_name = line.split('filename="')[1].split('"')[0]
|
||||
ext = os.path.splitext(orig_name)[1] or '.jpg'
|
||||
fname = uuid.uuid4().hex + ext
|
||||
file_data = data
|
||||
filename = fname
|
||||
break
|
||||
|
||||
if filename and file_data:
|
||||
path = os.path.join(PHOTO_DIR, filename)
|
||||
with open(path, 'wb') as f:
|
||||
f.write(file_data)
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(('{"path":"photos/' + filename + '"}').encode())
|
||||
else:
|
||||
self.send_error(400, 'No file found')
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
def do_DELETE(self):
|
||||
parsed = urllib.parse.urlparse(self.path)
|
||||
if parsed.path.startswith('/photos/'):
|
||||
rel = os.path.relpath(parsed.path, '/photos/')
|
||||
# basic path safety
|
||||
if '..' in rel or rel.startswith('/'):
|
||||
self.send_error(400)
|
||||
return
|
||||
filepath = os.path.join(PHOTO_DIR, rel)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'{"ok":true}')
|
||||
else:
|
||||
self.send_error(404)
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
def log_message(self, format, *args):
|
||||
if '/upload' in str(args[0]) or '/photos/' in str(args[0]):
|
||||
return # skip noisy upload logs
|
||||
super().log_message(format, *args)
|
||||
|
||||
|
||||
with socketserver.TCPServer(("", PORT), Handler) as httpd:
|
||||
print(f"Serving at http://0.0.0.0:{PORT}")
|
||||
|
||||
Reference in New Issue
Block a user