v2: чанки 30KB + MD5, /v2/chunk + /v2/finalize, Redis, worker, v2.0
This commit is contained in:
@@ -14,6 +14,7 @@ import mimeutil
|
||||
from test_routes import test_bp
|
||||
from upload import upload_bp
|
||||
from api import api_bp
|
||||
from v2.chunk_api import bp as v2_bp
|
||||
import redis_client
|
||||
import redis_worker
|
||||
|
||||
@@ -92,6 +93,7 @@ class ContractsApp:
|
||||
self.app.register_blueprint(test_bp)
|
||||
self.app.register_blueprint(upload_bp)
|
||||
self.app.register_blueprint(api_bp)
|
||||
self.app.register_blueprint(v2_bp)
|
||||
|
||||
def _health(self):
|
||||
return "OK", 200, {"Content-Type": "text/plain"}
|
||||
|
||||
+31
-19
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Сверка договоров</title>
|
||||
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script>
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||
<style>
|
||||
@@ -59,7 +60,7 @@
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<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.43</span></span>
|
||||
<span class="title">Сверка договоров <span style="font-weight:400;color:var(--muted);font-size:12px;">v2.0</span></span>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
@@ -155,52 +156,63 @@
|
||||
|
||||
window.removeFile = removeFile;
|
||||
|
||||
// ── Чанковая загрузка 30KB ─────────────────────────────────
|
||||
// ── Чанковая загрузка v2 (30KB + MD5) ────────────────────
|
||||
|
||||
function uploadFileChunked(file, cid, onProgress) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
var b64 = reader.result.split(',')[1];
|
||||
var buffer = reader.result;
|
||||
var bytes = new Uint8Array(buffer);
|
||||
var b64 = btoa(String.fromCharCode.apply(null, bytes));
|
||||
var checksum = SparkMD5.ArrayBuffer.hash(buffer);
|
||||
var CHUNK = 30 * 1024;
|
||||
var totalChunks = Math.ceil(b64.length / CHUNK);
|
||||
var uploadId = file.name + '_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
|
||||
var uploadId = 'u' + Date.now().toString(36) + Math.random().toString(36).substr(2, 7);
|
||||
var done = 0;
|
||||
|
||||
function sendChunk(i) {
|
||||
if (i >= totalChunks) { resolve({contract_id: cid}); return; }
|
||||
if (i >= totalChunks) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/v2/finalize');
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.timeout = 15000;
|
||||
xhr.onload = function() {
|
||||
try {
|
||||
var r = JSON.parse(xhr.responseText);
|
||||
if (r.ok) resolve({contract_id: r.contract_id});
|
||||
else reject(new Error(r.error));
|
||||
} catch(e) { reject(new Error('Bad JSON')); }
|
||||
};
|
||||
xhr.onerror = function() { reject(new Error('Сеть')); };
|
||||
xhr.ontimeout = function() { reject(new Error('Таймаут')); };
|
||||
xhr.send(JSON.stringify({upload_id: uploadId, filename: file.name, total: totalChunks, checksum: checksum}));
|
||||
return;
|
||||
}
|
||||
var piece = b64.substring(i * CHUNK, (i + 1) * CHUNK);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/chunk_json');
|
||||
xhr.open('POST', '/v2/chunk');
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.timeout = 10000;
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
var resp = JSON.parse(xhr.responseText);
|
||||
if (resp.error) { reject(new Error(resp.error)); return; }
|
||||
if (resp.contract_id) cid = resp.contract_id;
|
||||
var r = JSON.parse(xhr.responseText);
|
||||
if (!r.ok) { reject(new Error(r.error)); return; }
|
||||
done++;
|
||||
if (onProgress) onProgress(Math.round(done / totalChunks * 100));
|
||||
setTimeout(function() { sendChunk(i + 1); }, 100);
|
||||
setTimeout(function() { sendChunk(i + 1); }, 50);
|
||||
} catch(e) { reject(new Error('Bad JSON')); }
|
||||
} else { reject(new Error('HTTP ' + xhr.status)); }
|
||||
};
|
||||
xhr.onerror = function() { reject(new Error('Сеть')); };
|
||||
xhr.ontimeout = function() { reject(new Error('Таймаут')); };
|
||||
xhr.send(JSON.stringify({
|
||||
upload_id: uploadId,
|
||||
filename: file.name,
|
||||
chunk_index: i,
|
||||
total_chunks: totalChunks,
|
||||
data: piece,
|
||||
cid: cid || null
|
||||
}));
|
||||
xhr.send(JSON.stringify({upload_id: uploadId, index: i, total: totalChunks, data: piece}));
|
||||
}
|
||||
sendChunk(0);
|
||||
};
|
||||
reader.onerror = function() { reject(new Error('Read error')); };
|
||||
reader.readAsDataURL(file);
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user