Как передать бинарный файл в POST?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Передача бинарных файлов в POST: технические подходы
Передача бинарных файлов (изображения, документы, видео, архивы) — частая задача в API. Существует несколько подходов, каждый с преимуществами и недостатками.
Подход 1: multipart/form-data (Рекомендуется)
Описание
Это стандартный способ передачи файлов через формы в HTML и API. Данные кодируются как multipart MIME-сообщение.
Пример HTTP запроса:
POST /api/v1/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Content-Length: 12345
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg
[BINARY DATA HERE]
------WebKitFormBoundary
Content-Disposition: form-data; name="description"
My photo
------WebKitFormBoundary--
Преимущества:
- Стандарт для загрузки файлов
- Поддерживается везде (браузеры, curl, библиотеки)
- Можно передать несколько файлов одновременно
- Можно добавить дополнительные поля (метаданные)
- Метаданные передаются отдельно от бинарных данных
Недостатки:
- Больший overhead из-за boundary разделителей
- Сложнее отладить вручную
Пример на разных языках:
curl:
curl -X POST http://localhost:3000/upload \
-F "file=@photo.jpg" \
-F "description=My photo"
Python (requests):
import requests
with open('photo.jpg', 'rb') as f:
files = {'file': f}
data = {'description': 'My photo'}
response = requests.post('http://localhost:3000/upload',
files=files, data=data)
JavaScript (fetch):
const formData = new FormData();
const fileInput = document.querySelector('input[type="file"]');
formData.append('file', fileInput.files[0]);
formData.append('description', 'My photo');
fetch('/api/v1/upload', {
method: 'POST',
body: formData
}).then(r => r.json());
FastAPI (Python):
from fastapi import FastAPI, UploadFile, File, Form
@app.post("/upload")
async def upload(file: UploadFile = File(...),
description: str = Form(...)):
contents = await file.read()
# Сохраняем файл
return {"filename": file.filename, "size": len(contents)}
Подход 2: application/octet-stream (Raw binary)
Описание
Передача чистого бинарного содержимого без каких-либо оберток.
Пример HTTP запроса:
POST /api/v1/upload HTTP/1.1
Host: api.example.com
Content-Type: application/octet-stream
Content-Length: 1024
[BINARY DATA HERE]
Преимущества:
- Минимальный overhead
- Быстро и эффективно
- Простая отладка (чистые бинарные данные)
Недостатки:
- Нельзя передать метаданные в теле (только в headers)
- Нельзя передать несколько файлов одновременно
- Нужно передавать имя файла в заголовке
Пример:
curl:
curl -X POST http://localhost:3000/upload \
-H "Content-Type: application/octet-stream" \
-H "X-Filename: photo.jpg" \
--data-binary @photo.jpg
FastAPI:
@app.post("/upload")
async def upload(request: Request):
body = await request.body()
filename = request.headers.get('X-Filename')
# Сохраняем файл
return {"filename": filename, "size": len(body)}
Подход 3: Base64 кодирование в JSON
Описание
Преобразование бинарных данных в Base64 строку и отправка в JSON.
Пример:
{
"file": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"filename": "photo.jpg",
"mimeType": "image/jpeg"
}
Преимущества:
- Удобно для API, которые принимают JSON
- Легко отправить из JavaScript
- Все метаданные в одном JSON
Недостатки:
- Base64 увеличивает размер на 33%
- Медленнее чем бинарные данные
- Много памяти для больших файлов
Пример:
JavaScript:
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result.split(',')[1];
fetch('/api/v1/upload', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
file: base64,
filename: file.name,
mimeType: file.type
})
});
};
reader.readAsDataURL(file);
FastAPI:
import base64
from pydantic import BaseModel
class FileUpload(BaseModel):
file: str # base64
filename: str
mimeType: str
@app.post("/upload")
async def upload(data: FileUpload):
binary_data = base64.b64decode(data.file)
# Сохраняем файл
return {"filename": data.filename, "size": len(binary_data)}
Подход 4: Chunked upload (для больших файлов)
Описание
Разбиение большого файла на маленькие части (chunks) и загрузка их последовательно.
Процесс:
1. Клиент инициирует upload → получает upload_id
2. Отправляет chunk 1 (1MB)
3. Отправляет chunk 2 (1MB)
4. Отправляет chunk 3 (1MB)
...
N. Завершает upload → сервер объединяет chunks
Преимущества:
- Можно загрузить файлы > памяти сервера
- Resume capability (если разорвалось соединение)
- Лучше для нестабильной сети
- Показывать progress bar
Пример API:
Шаг 1. POST /uploads → {"upload_id": "abc123"}
Шаг 2. PUT /uploads/abc123/chunks/1 (бинарные данные)
Шаг 3. PUT /uploads/abc123/chunks/2 (бинарные данные)
...
Шаг N. POST /uploads/abc123/complete
Таблица сравнения подходов
Подход | Размер файла | Сложность | Производительность
─────────────────────────────────────────────────────────────────
multipart/form-data | до 2-5GB | Средняя | Хорошая
application/octet | до 2-5GB | Простая | Отличная
Base64 в JSON | < 100MB | Простая | Плохая
Chunked upload | Любой | Высокая | Хорошая
Best Practices для System Analyst
При проектировании API файлозагрузки:
1. Выберите правильный подход:
- Файлы < 100MB + нужны метаданные → multipart/form-data
- Файлы < 100MB + нет метаданных → octet-stream
- Файлы < 5MB + JavaScript от браузера → Base64 в JSON
- Файлы > 500MB → Chunked upload
2. Безопасность:
- Валидируй MIME-type (не доверяй расширению)
- Устанавливай лимит на размер файла
- Сканируй на вирусы
- Сохраняй в безопасном месте (вне web root)
- Проверь права доступа при скачивании
3. Производительность:
- Не загружай в память (streaming)
- Используй CDN для раздачи файлов
- Сжимай изображения
- Используй presigned URLs для S3
4. Обработка ошибок:
- Валидируй до сохранения
- Используй временные файлы
- Удаляй неполные загрузки
- Логируй все ошибки
5. Documentation:
Эндпоинт: POST /api/v1/files/upload
Метод: multipart/form-data
Поля:
- file (required): бинарный файл, макс 10MB
- description (optional): текстовое описание
Ответ: {"id": "uuid", "url": "http://..."}
Заключение
Рекомендация: в большинстве случаев используйте multipart/form-data — это стандарт для файлозагрузок, поддерживается везде, и позволяет передавать файлы вместе с метаданными. Для специальных случаев (большие файлы, нестабильная сеть) используйте chunked upload.