fix: XHR → fetch() for upload — drhider v0.0.29 fix for ERR_CONNECTION_RESET
Deploy contracts-flask / validate (push) Successful in 0s
Deploy contracts-flask / validate (push) Successful in 0s
This commit is contained in:
+52
-55
@@ -30,7 +30,7 @@
|
|||||||
function statusToHTML(st) {
|
function statusToHTML(st) {
|
||||||
if (!st || !st.kind) return '';
|
if (!st || !st.kind) return '';
|
||||||
switch (st.kind) {
|
switch (st.kind) {
|
||||||
case 'uploading': return '↑ ' + (st.pct || 0) + '%';
|
case 'uploading': return '⏳ загрузка...';
|
||||||
case 'uploaded': return '<span class="status-ok">✓</span>';
|
case 'uploaded': return '<span class="status-ok">✓</span>';
|
||||||
case 'unzipping': return '⏳ распаковка...';
|
case 'unzipping': return '⏳ распаковка...';
|
||||||
case 'parsing': return '⏳ парсинг...';
|
case 'parsing': return '⏳ парсинг...';
|
||||||
@@ -208,64 +208,62 @@ window.toggleClassifyDetail = async function(i) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* uploadFile(file, onProgress) — XHR-загрузка одного файла на бэкенд.
|
* uploadFile(file, onProgress) — загрузка одного файла на бэкенд.
|
||||||
|
*
|
||||||
|
* v2.0.1: fetch() вместо XHR — XHR давал ERR_CONNECTION_RESET (drhider v0.0.29)
|
||||||
|
* Прогресс: без процентов (fetch не поддерживает), только статус.
|
||||||
*
|
*
|
||||||
* Особенности:
|
* Особенности:
|
||||||
* - .doc (не .docx!) конвертируется через CONVERT_URL перед загрузкой
|
* - .doc (не .docx!) конвертируется через CONVERT_URL перед загрузкой
|
||||||
* - onProgress(pct) — callback с процентом загрузки (0-100)
|
* - onProgress(pct) — сохранён для совместимости, вызывается с 0 и 100
|
||||||
* - Возвращает Promise<ответ API> с полями doc_id, contract_id, parsed
|
* - Возвращает Promise<ответ API>
|
||||||
* - Таймаут 180с (большие PDF)
|
|
||||||
*/
|
*/
|
||||||
function uploadFile(file, onProgress, zipSource) {
|
function uploadFile(file, onProgress, zipSource) {
|
||||||
return new Promise(function(resolve, reject) {
|
// .doc → конвертация в docx (старый формат Word)
|
||||||
// .doc → конвертация в docx (старый формат Word)
|
var isDoc = file.name.toLowerCase().endsWith('.doc') && !file.name.toLowerCase().endsWith('.docx');
|
||||||
var isDoc = file.name.toLowerCase().endsWith('.doc') && !file.name.toLowerCase().endsWith('.docx');
|
|
||||||
var uploadFile = file;
|
|
||||||
var uploadName = file.name;
|
|
||||||
|
|
||||||
function doUpload() {
|
function doUpload(uploadFile, uploadName) {
|
||||||
var xhr = new XMLHttpRequest();
|
if (onProgress) onProgress(0);
|
||||||
var fd = new FormData();
|
var fd = new FormData();
|
||||||
fd.append('files', uploadFile, uploadName);
|
fd.append('files', uploadFile, uploadName);
|
||||||
if (state.contractId) fd.append('contract_id', state.contractId);
|
if (state.contractId) fd.append('contract_id', state.contractId);
|
||||||
fd.append('batch_id', state.batchId);
|
fd.append('batch_id', state.batchId);
|
||||||
if (zipSource) fd.append('zip_source', zipSource);
|
if (zipSource) fd.append('zip_source', zipSource);
|
||||||
xhr.open('POST', UPLOAD_URL);
|
|
||||||
xhr.upload.onprogress = function(e) {
|
|
||||||
if (e.lengthComputable && onProgress) onProgress(Math.round(e.loaded / e.total * 100));
|
|
||||||
};
|
|
||||||
xhr.onload = function() {
|
|
||||||
try {
|
|
||||||
var r = JSON.parse(xhr.responseText);
|
|
||||||
if (r.ok) resolve(r);
|
|
||||||
else reject(new Error(r.error || 'Неизвестная ошибка'));
|
|
||||||
} catch(e) { reject(new Error('Некорректный ответ')); }
|
|
||||||
};
|
|
||||||
xhr.onerror = function() { reject(new Error('Сеть')); };
|
|
||||||
xhr.ontimeout = function() { reject(new Error('Таймаут')); };
|
|
||||||
xhr.timeout = 180000;
|
|
||||||
xhr.send(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDoc) {
|
return fetch(UPLOAD_URL + '?_=' + Date.now(), { method: 'POST', body: fd })
|
||||||
var xhr = new XMLHttpRequest();
|
.then(function(r) {
|
||||||
xhr.open('POST', CONVERT_URL);
|
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||||
xhr.responseType = 'blob';
|
return r.json();
|
||||||
xhr.onload = function() {
|
})
|
||||||
if (xhr.status === 200 && xhr.response.size > 100) {
|
.then(function(data) {
|
||||||
uploadFile = xhr.response;
|
if (data.ok) {
|
||||||
uploadName = file.name.replace(/\.doc$/i, '.docx');
|
if (onProgress) onProgress(100);
|
||||||
doUpload();
|
return data;
|
||||||
} else {
|
|
||||||
reject(new Error('Конвертация .doc'));
|
|
||||||
}
|
}
|
||||||
};
|
throw new Error(data.error || 'Неизвестная ошибка');
|
||||||
xhr.onerror = function() { reject(new Error('Конвертер')); };
|
})
|
||||||
xhr.send(file);
|
.catch(function(e) {
|
||||||
} else {
|
// Перезапись fetch-ошибок в понятные сообщения
|
||||||
doUpload();
|
if (e.message === 'Failed to fetch' || e.name === 'TypeError') {
|
||||||
}
|
throw new Error('Сеть');
|
||||||
});
|
}
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDoc) {
|
||||||
|
return fetch(CONVERT_URL, { method: 'POST', body: file })
|
||||||
|
.then(function(r) {
|
||||||
|
if (!r.ok) throw new Error('Конвертация .doc');
|
||||||
|
return r.blob();
|
||||||
|
})
|
||||||
|
.then(function(blob) {
|
||||||
|
if (blob.size < 100) throw new Error('Конвертация .doc');
|
||||||
|
return doUpload(blob, file.name.replace(/\.doc$/i, '.docx'));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return doUpload(file, file.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -425,7 +423,7 @@ async function addZipFile(file) {
|
|||||||
*/
|
*/
|
||||||
async function addRegularFile(file, zipSource) {
|
async function addRegularFile(file, zipSource) {
|
||||||
// Создать запись с начальным статусом
|
// Создать запись с начальным статусом
|
||||||
var entry = { name: file.name, lastModified: file.lastModified, size: file.size, file: file, status: { kind: 'uploading', pct: 0 }, zip_source: zipSource || null };
|
var entry = { name: file.name, lastModified: file.lastModified, size: file.size, file: file, status: { kind: 'uploading' }, zip_source: zipSource || null };
|
||||||
|
|
||||||
// ДЕДУПЛИКАЦИЯ: ключ = (zip_source, name), чтобы одноимённые файлы из разных ZIP не затирались
|
// ДЕДУПЛИКАЦИЯ: ключ = (zip_source, name), чтобы одноимённые файлы из разных ZIP не затирались
|
||||||
var dupKey = (zipSource || '') + '/' + file.name;
|
var dupKey = (zipSource || '') + '/' + file.name;
|
||||||
@@ -449,9 +447,8 @@ async function addRegularFile(file, zipSource) {
|
|||||||
render(state);
|
render(state);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// XHR-загрузка с прогрессом
|
// fetch-загрузка (v2.0.1: XHR заменён на fetch — drhider v0.0.29)
|
||||||
var resp = await uploadFile(file, function(pct) {
|
var resp = await uploadFile(file, function() {
|
||||||
state.files[rowIdx].status = { kind: 'uploading', pct: pct };
|
|
||||||
render(state);
|
render(state);
|
||||||
}, zipSource);
|
}, zipSource);
|
||||||
// Бэкенд возвращает doc_id и contract_id (нижний регистр — Python keys)
|
// Бэкенд возвращает doc_id и contract_id (нижний регистр — Python keys)
|
||||||
|
|||||||
Reference in New Issue
Block a user