""" LoadTest - сервис проверки загрузки файлов на платформу Nubes (Flask). ЦЕЛЬ: проверить, доходят ли файлы (в т.ч. большие) до бэкенда или нет. Файл принимается в ПАМЯТЬ (без записи на диск) и возвращается МАКСИМУМ статистики. Обязательная структура для платформы Nubes: repo/ ├── requirements.txt └── site/ ├── app.py <- этот файл (точка входа: python site/app.py) ├── static/style.css └── templates/index.html """ import hashlib import time from flask import Flask, jsonify, render_template, request # Явно указываем папки шаблонов и статики - # платформа запускает `python site/app.py` из корня репо, иначе Flask не найдёт шаблоны. app = Flask(__name__, template_folder="templates", static_folder="static") # ВАЖНО: НЕ ставим MAX_CONTENT_LENGTH (по умолчанию лимита нет). # Цель - проверить РЕАЛЬНЫЕ лимиты платформы/прокси, а не собственный лимит Flask. @app.route("/") def index(): """Главная страница - форма выбора файла и загрузки.""" return render_template("index.html") @app.route("/health") def health(): """Проверка живости для платформы.""" return "ok" @app.route("/upload", methods=["POST"]) def upload(): """Приём файла в память + сбор максимальной статистики.""" t0 = time.time() # момент начала обработки запроса на сервере # Метаданные запроса (заголовки, адрес клиента) - для диагностики. meta = { "server_start": time.strftime("%Y-%m-%d %H:%M:%S"), "remote_addr": request.remote_addr or "", "content_length_header": request.headers.get("Content-Length", ""), "content_type_header": request.headers.get("Content-Type", ""), "user_agent": request.headers.get("User-Agent", ""), } # Валидация: должен прийти multipart-файл в поле "file". if "file" not in request.files: return jsonify({"ok": False, "error": "Нет поля 'file' в запросе", "meta": meta}), 400 f = request.files["file"] if not f or f.filename == "": return jsonify({"ok": False, "error": "Пустое имя файла", "meta": meta}), 400 # Читаем ВЕСЬ файл в память и замеряем время чтения. t_read = time.time() data = f.read() read_s = time.time() - t_read size = len(data) # сколько байт РЕАЛЬНО дошло до сервера total_s = time.time() - t0 # полное время обработки запроса на сервере result = { "ok": True, "file": { "name": f.filename, "mime": f.mimetype or "", "size_bytes": size, "size_mb": round(size / (1024 * 1024), 3), }, "server": { "read_ms": round(read_s * 1000, 2), # время чтения в память "total_ms": round(total_s * 1000, 2), # полное время обработки "speed_mb_s": round(size / (1024 * 1024) / read_s, 3) if read_s > 0 else 0, }, "md5": hashlib.md5(data).hexdigest(), # контрольная сумма принятых байт "meta": meta, } return jsonify(result) if __name__ == "__main__": # ОБЯЗАТЕЛЬНО для платформы: `python site/app.py` должен стартовать сервер. # debug=False - прод-режим, без reloader (иначе поднимается второй процесс). app.run(host="0.0.0.0", port=5000, debug=False)