diff --git a/deploy/convert_server.py b/deploy/convert_server.py index 9bc49cb..9b73019 100755 --- a/deploy/convert_server.py +++ b/deploy/convert_server.py @@ -280,35 +280,63 @@ class Handler(BaseHTTPRequestHandler): self.wfile.write(json.dumps({"error": str(e)}, ensure_ascii=False).encode()) def _handle_upload(self): - """Принять файл и передать в Lucee через JSON API (обходит multipart-краш Lucee).""" + """Принять файл и передать в Lucee чанками (обходит лимит тела запроса).""" + import uuid from email.parser import BytesParser content_type = self.headers.get("Content-Type", "") length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) - # Парсим multipart msg = BytesParser().parsebytes( b"Content-Type: " + content_type.encode() + b"\r\n\r\n" + body ) filename = None file_data = None + contract_id = "" if msg.is_multipart(): for part in msg.get_payload(): fn = part.get_filename() if fn: filename = fn file_data = part.get_payload(decode=True) - break + else: + # Может быть поле contract_id + disp = part.get("Content-Disposition", "") + if "name=\"contract_id\"" in disp: + contract_id = part.get_payload(decode=True).decode("utf-8", errors="ignore").strip() if not filename or not file_data: self._send_error_cors(400, "no file in request") return - # Шлём в Lucee через JSON API (не multipart!) + # Режем на чанки по 12KB (hex = 24KB, влезает в лимит Lucee ~50KB) + CHUNK = 12000 + upload_id = str(uuid.uuid4()) + raw = file_data + total = (len(raw) + CHUNK - 1) // CHUNK + + for i in range(total): + chunk = raw[i*CHUNK:(i+1)*CHUNK] + r = httpx.post( + f"{LUCEE_URL}/chunk.cfm?action=put", + json={ + "upload_id": upload_id, + "chunk_index": i, + "total_chunks": total, + "filename": filename, + "data": chunk.hex(), + }, + timeout=30 + ) + if r.status_code != 200: + self._send_error_cors(502, f"chunk {i+1}/{total} failed: HTTP {r.status_code}") + return + + # Собрать чанки в Lucee resp = httpx.post( - f"{LUCEE_URL}/upload.cfm", - json={"filename": filename, "data": "\\x" + file_data.hex()}, + f"{LUCEE_URL}/chunk.cfm?action=assemble", + json={"upload_id": upload_id, "contract_id": contract_id}, timeout=60 ) if resp.status_code == 200: @@ -318,7 +346,7 @@ class Handler(BaseHTTPRequestHandler): self.end_headers() self.wfile.write(resp.content) else: - self._send_error_cors(502, f"Lucee error: {resp.status_code}") + self._send_error_cors(502, f"assemble failed: HTTP {resp.status_code}") def _handle_unzip_upload(self): """Распаковать ZIP и загрузить каждый файл в Lucee. Только файлы из корня архива.""" diff --git a/index.cfm b/index.cfm index c2feca4..148c8bd 100644 --- a/index.cfm +++ b/index.cfm @@ -75,7 +75,7 @@
Nubes - Сверка договоров — LLM AI-driven Event Sourcing v1.0.134 — Lucee + Сверка договоров — LLM AI-driven Event Sourcing v1.0.136 — Lucee