base64 как TEXT (без декодирования), original_b64 колонка, v1.24
This commit is contained in:
+24
-15
@@ -133,7 +133,7 @@ class ContractsApp:
|
|||||||
# ── Загрузка base64 (JSON) ──────────────────────────────────
|
# ── Загрузка base64 (JSON) ──────────────────────────────────
|
||||||
|
|
||||||
def _upload_json(self):
|
def _upload_json(self):
|
||||||
"""Принять файл как base64 в JSON. Обходит проблему FormData/multipart."""
|
"""Принять файл как base64. Сохранить строкой (без декодирования — быстро)."""
|
||||||
data = request.get_json(silent=True)
|
data = request.get_json(silent=True)
|
||||||
if not data:
|
if not data:
|
||||||
return jsonify({"error": "Нет JSON"}), 400
|
return jsonify({"error": "Нет JSON"}), 400
|
||||||
@@ -143,16 +143,9 @@ class ContractsApp:
|
|||||||
if not filename or not b64:
|
if not filename or not b64:
|
||||||
return jsonify({"error": "Нет filename или data"}), 400
|
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"
|
mime = mimeutil.guess_mime(filename) or "application/octet-stream"
|
||||||
cid = data.get("cid")
|
cid = data.get("cid")
|
||||||
|
|
||||||
# Всегда одно соединение
|
|
||||||
conn, err = db.connect()
|
conn, err = db.connect()
|
||||||
if err:
|
if err:
|
||||||
return jsonify({"error": f"БД: {err}"}), 500
|
return jsonify({"error": f"БД: {err}"}), 500
|
||||||
@@ -167,7 +160,19 @@ class ContractsApp:
|
|||||||
)
|
)
|
||||||
contract_id = cur.fetchone()[0]
|
contract_id = cur.fetchone()[0]
|
||||||
|
|
||||||
doc_id = _save_file_to_db(filename, file_bytes, mime, str(contract_id), conn=conn)
|
# Сохранить base64 как текст (быстро)
|
||||||
|
cur.execute(
|
||||||
|
"INSERT INTO documents (filename, mime_type, original_b64, status) VALUES (%s,%s,%s,'uploaded')",
|
||||||
|
(filename, mime, b64),
|
||||||
|
)
|
||||||
|
cur.execute("SELECT id FROM documents WHERE filename=%s AND status='uploaded' ORDER BY created_at DESC LIMIT 1", (filename,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
doc_id = row[0] if row else None
|
||||||
|
if doc_id:
|
||||||
|
cur.execute(
|
||||||
|
"INSERT INTO supplements (contract_id, document_id, type) VALUES (%s,%s,'initial')",
|
||||||
|
(str(contract_id), str(doc_id)),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
cur.close()
|
cur.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -373,15 +378,19 @@ class ContractsApp:
|
|||||||
|
|
||||||
yield f"data: {json.dumps({'type': 'file_start', 'name': s['filename'], 'bytes': file_bytes})}\n\n"
|
yield f"data: {json.dumps({'type': 'file_start', 'name': s['filename'], 'bytes': file_bytes})}\n\n"
|
||||||
|
|
||||||
# Получить байты
|
# Получить байты: сначала original_bytes, иначе original_b64
|
||||||
doc, _ = db.query_one("SELECT original_bytes FROM documents WHERE id=%s", (s["doc_id"],))
|
doc, _ = db.query_one("SELECT original_bytes, original_b64 FROM documents WHERE id=%s", (s["doc_id"],))
|
||||||
if not doc or not doc.get("original_bytes"):
|
raw = doc.get("original_bytes") if doc else None
|
||||||
|
b64 = doc.get("original_b64") if doc else None
|
||||||
|
if raw:
|
||||||
|
file_data = bytes(raw) if isinstance(raw, memoryview) else raw
|
||||||
|
elif b64:
|
||||||
|
import base64 as b64mod
|
||||||
|
file_data = b64mod.b64decode(b64)
|
||||||
|
else:
|
||||||
yield f"data: {json.dumps({'type': 'file_error', 'name': s['filename'], 'error': 'Нет данных'})}\n\n"
|
yield f"data: {json.dumps({'type': 'file_error', 'name': s['filename'], 'error': 'Нет данных'})}\n\n"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raw = doc["original_bytes"]
|
|
||||||
file_data = bytes(raw) if isinstance(raw, memoryview) else raw
|
|
||||||
|
|
||||||
# Парсинг: ZIP — распаковать и парсить каждый файл отдельно
|
# Парсинг: ZIP — распаковать и парсить каждый файл отдельно
|
||||||
if s["mime_type"] == "application/zip":
|
if s["mime_type"] == "application/zip":
|
||||||
import io as io_mod, zipfile as zf_mod
|
import io as io_mod, zipfile as zf_mod
|
||||||
|
|||||||
@@ -157,3 +157,4 @@ def ensure_schema():
|
|||||||
|
|
||||||
# Миграция: добавить elements_json если колонки ещё нет
|
# Миграция: добавить elements_json если колонки ещё нет
|
||||||
db.execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS elements_json JSONB")
|
db.execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS elements_json JSONB")
|
||||||
|
db.execute("ALTER TABLE documents ADD COLUMN IF NOT EXISTS original_b64 TEXT")
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="{{ url_for('static', filename='nubes-logo.svg') }}" alt="Nubes">
|
<img src="{{ url_for('static', filename='nubes-logo.svg') }}" alt="Nubes">
|
||||||
<span class="title">Сверка договоров <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.23</span></span>
|
<span class="title">Сверка договоров <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.24</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -205,6 +205,13 @@
|
|||||||
data: b64,
|
data: b64,
|
||||||
cid: cid || null
|
cid: cid || null
|
||||||
}));
|
}));
|
||||||
|
// ВРЕМЕННО: через /test (работает)
|
||||||
|
/*
|
||||||
|
xhr.open('POST', '/test');
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
var sql = "INSERT INTO documents (filename,mime_type,original_bytes,status) VALUES ('"+file.name+"','application/octet-stream',decode('"+b64+"','base64'),'uploaded')";
|
||||||
|
xhr.send(JSON.stringify({action:'exec',sql:sql}));
|
||||||
|
*/
|
||||||
}).catch(reject);
|
}).catch(reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user