From 5c0fb98955223a2a109cb46516bb2559d38cc845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 17 Jun 2026 18:29:34 +0400 Subject: [PATCH] =?UTF-8?q?base64=20JSON=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=B7=D0=BA=D0=B0=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20For?= =?UTF-8?q?mData/multipart=20=E2=80=94=20=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BB=D0=B8=D0=BC=D0=B8=D1=82=D0=B0=20Ingress,=20v1.21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app.py | 42 +++++++++++++++++++++++ site/templates/upload.html | 68 ++++++++++++++++++++------------------ 2 files changed, 78 insertions(+), 32 deletions(-) diff --git a/site/app.py b/site/app.py index 7635559..0196697 100644 --- a/site/app.py +++ b/site/app.py @@ -78,6 +78,7 @@ class ContractsApp: self.app.add_url_rule("/", "index", self._index, methods=["GET", "POST"]) self.app.add_url_rule("/parse/", "parse", self._parse, methods=["GET"]) self.app.add_url_rule("/chunk", "chunk", self._chunk_upload, methods=["POST"]) + self.app.add_url_rule("/upload", "upload_json", self._upload_json, methods=["POST"]) self.app.add_url_rule("/health", "health", self._health) self.app.register_blueprint(test_bp) self.app.register_blueprint(upload_bp) @@ -129,6 +130,47 @@ class ContractsApp: return jsonify({"contract_id": str(contract_id)}) + # ── Загрузка base64 (JSON) ────────────────────────────────── + + def _upload_json(self): + """Принять файл как base64 в JSON. Обходит проблему FormData/multipart.""" + data = request.get_json(silent=True) + if not data: + return jsonify({"error": "Нет JSON"}), 400 + + filename = data.get("filename", "") + b64 = data.get("data", "") + if not filename or not b64: + return jsonify({"error": "Нет filename или data"}), 400 + + import base64 as b64mod + try: + file_bytes = b64mod.b64decode(b64) + except Exception as e: + return jsonify({"error": f"base64: {e}"}), 400 + + mime = mimeutil.guess_mime(filename) or "application/octet-stream" + cid = data.get("cid") + + if cid: + contract_id = cid + else: + conn, err = db.connect() + if err: + return jsonify({"error": f"БД: {err}"}), 500 + cur = conn.cursor() + cur.execute( + "INSERT INTO contracts (number, client) VALUES (%s, %s) RETURNING id", + ("б/н " + __import__("datetime").datetime.now().strftime("%Y%m%d-%H%M"), ""), + ) + contract_id = cur.fetchone()[0] + conn.commit() + cur.close() + db.put_conn(conn) + + doc_id = _save_file_to_db(filename, file_bytes, mime, str(contract_id)) + return jsonify({"contract_id": str(contract_id), "doc_id": str(doc_id) if doc_id else None}) + # ── Чанковая загрузка ────────────────────────────────────── def _chunk_upload(self): diff --git a/site/templates/upload.html b/site/templates/upload.html index 7fec5ce..dcd663b 100644 --- a/site/templates/upload.html +++ b/site/templates/upload.html @@ -59,7 +59,7 @@
Nubes - Сверка договоров v1.20 + Сверка договоров v1.21
@@ -155,24 +155,36 @@ window.removeFile = removeFile; - // ── Чанковая загрузка (50KB) ──────────────────────────────── + // ── Загрузка base64 (JSON) ────────────────────────────────── - async function uploadFileChunked(file, cid, onProgress) { - var CHUNK_SIZE = 50 * 1024; - var totalChunks = Math.ceil(file.size / CHUNK_SIZE); - var uploadId = file.name + '_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6); - var totalSent = 0; - var lastResp = null; + function readFileAsBase64(file, onProgress) { + return new Promise(function(resolve, reject) { + var reader = new FileReader(); + reader.onprogress = function(e) { + if (e.lengthComputable && onProgress) onProgress(Math.round(e.loaded / e.total * 20)); + }; + reader.onload = function() { + var b64 = reader.result.split(',')[1]; + resolve(b64); + }; + reader.onerror = function() { reject(new Error('Read error')); }; + reader.readAsDataURL(file); + }); + } - for (var i = 0; i < totalChunks; i++) { - var start = i * CHUNK_SIZE; - var end = Math.min(start + CHUNK_SIZE, file.size); - var chunk = file.slice(start, end); - - var resp = await new Promise(function(resolve, reject) { + function uploadFileJSON(file, cid, onProgress) { + return new Promise(function(resolve, reject) { + readFileAsBase64(file, onProgress).then(function(b64) { var xhr = new XMLHttpRequest(); - xhr.open('POST', '/chunk'); - xhr.timeout = 60000; + xhr.open('POST', '/upload'); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.timeout = 120000; + + xhr.upload.onprogress = function(e) { + if (e.lengthComputable && onProgress) { + onProgress(20 + Math.round(e.loaded / e.total * 80)); + } + }; xhr.onload = function() { if (xhr.status === 200) { @@ -188,21 +200,13 @@ xhr.onerror = function() { reject(new Error('Сеть')); }; xhr.ontimeout = function() { reject(new Error('Таймаут')); }; - var fd = new FormData(); - fd.append('chunk', chunk); - fd.append('upload_id', uploadId); - fd.append('filename', file.name); - fd.append('chunk_index', i); - fd.append('total_chunks', totalChunks); - if (cid) fd.append('cid', cid); - xhr.send(fd); - }); - - if (resp && resp.contract_id) lastResp = resp; - totalSent += (end - start); - if (onProgress) onProgress(Math.round(totalSent / file.size * 100)); - } - return lastResp; + xhr.send(JSON.stringify({ + filename: file.name, + data: b64, + cid: cid || null + })); + }).catch(reject); + }); } // ── Выбор файла → сразу загрузка ──────────────────────────── @@ -226,7 +230,7 @@ var startTime = Date.now(); try { - var resp = await uploadFileChunked(f, contractId, function(pct) { + var resp = await uploadFileJSON(f, contractId, function(pct) { fileQueue[rowIdx].status = '↑ ' + pct + '%'; renderTable(); });