From 699a73d48d88667b5df2a24d65c5ff2fe18bc394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Fri, 10 Jul 2026 09:00:02 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20base64-=D1=80=D0=B5=D0=B6=D0=B8=D0=BC?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20(=D0=BE?= =?UTF-8?q?=D0=B1=D1=85=D0=BE=D0=B4=20DDOS-Guard),=20v1.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app.py | 13 +++++++- site/static/app.js | 80 +++++++++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/site/app.py b/site/app.py index 6ff152d..9f0be6a 100644 --- a/site/app.py +++ b/site/app.py @@ -1,9 +1,10 @@ import time +import base64 from flask import Flask, render_template, request app = Flask(__name__) -VERSION = '1.0.2' +VERSION = '1.0.3' @app.route('/') @@ -20,6 +21,16 @@ def health(): def upload(): t0 = time.time() total = 0 + # base64-режим: клиент отправляет поле "data" + data = request.form.get('data', '') + if data: + # убрать префикс data:...;base64, + if ',' in data: + data = data.split(',', 1)[1] + raw = base64.b64decode(data) + elapsed = round((time.time() - t0) * 1000) + return {'ok': True, 'bytes': len(raw), 'elapsed_ms': elapsed} + # старый режим: файл напрямую (может не работать через DDOS-Guard) for f in request.files.values(): while True: chunk = f.read(8192) diff --git a/site/static/app.js b/site/static/app.js index 74210bc..af5fa0c 100644 --- a/site/static/app.js +++ b/site/static/app.js @@ -180,48 +180,64 @@ async function uploadAll() { fileSummary.textContent = summary; } -/** uploadOne(fileEntry, idx) — XHR одного файла, возвращает {bytes, elapsed_ms} */ +/** uploadOne(fileEntry, idx) — читает файл в base64, отправляет как поле формы */ function uploadOne(f, idx) { return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - var fd = new FormData(); - fd.append('file', f.file, f.name); + // Шаг 1: прочитать файл в base64 + f.status = 'uploading'; + f.progress = 0; + renderTable(); - var t0 = Date.now(); + var reader = new FileReader(); + reader.onload = function () { + // base64-строка готова, теперь отправляем + var t0 = Date.now(); + var xhr = new XMLHttpRequest(); + var fd = new FormData(); + fd.append('data', reader.result); + fd.append('name', f.name); + fd.append('orig_size', f.size); - xhr.open('POST', '/upload'); + xhr.open('POST', '/upload'); - xhr.upload.onprogress = function (e) { - if (e.lengthComputable) { - f.progress = Math.round(e.loaded / e.total * 100); - renderTable(); - } - }; - - xhr.onload = function () { - try { - var r = JSON.parse(xhr.responseText); - if (r.ok) { - resolve({ bytes: r.bytes, elapsed_ms: r.elapsed_ms }); - } else { - reject(new Error(r.error || 'Ошибка сервера')); + xhr.upload.onprogress = function (e) { + if (e.lengthComputable) { + f.progress = Math.round(e.loaded / e.total * 100); + renderTable(); } - } catch (e) { - reject(new Error('Некорректный ответ')); - } + }; + + xhr.onload = function () { + try { + var r = JSON.parse(xhr.responseText); + if (r.ok) { + resolve({ bytes: r.bytes, elapsed_ms: r.elapsed_ms }); + } else { + reject(new Error(r.error || 'Ошибка сервера')); + } + } catch (e) { + reject(new Error('Некорректный ответ')); + } + }; + + xhr.onerror = function () { + var elapsed = Date.now() - t0; + reject(new Error('Сеть (' + (elapsed / 1000).toFixed(0) + 'с)')); + }; + + xhr.ontimeout = function () { + reject(new Error('Таймаут (' + (xhr.timeout / 1000) + 'с)')); + }; + + xhr.timeout = 600000; // 10 минут (base64 +33%) + xhr.send(fd); }; - xhr.onerror = function () { - var elapsed = Date.now() - t0; - reject(new Error('Сеть (' + (elapsed / 1000).toFixed(0) + 'с)')); + reader.onerror = function () { + reject(new Error('Ошибка чтения файла')); }; - xhr.ontimeout = function () { - reject(new Error('Таймаут (' + (xhr.timeout / 1000) + 'с)')); - }; - - xhr.timeout = 300000; // 5 минут на файл - xhr.send(fd); + reader.readAsDataURL(f.file); }); }