← Назад к вопросам
Как используя REST отправить файл на backend?
1.7 Middle🔥 211 комментариев
#REST API и HTTP
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Отправка файлов через REST API: полный гайд
Отправка файлов через REST — это стандартная операция, которая использует multipart/form-data. Рассмотрим различные подходы и лучшие практики.
1. Отправка файла с помощью requests (Python клиент)
Простой пример:
import requests
# Отправка одного файла
with open('document.pdf', 'rb') as f:
files = {'file': f}
response = requests.post('http://api.example.com/upload', files=files)
print(response.json())
Отправка файла с метаданными:
with open('photo.jpg', 'rb') as f:
files = {'image': f}
data = {'user_id': 123, 'title': 'My Photo'}
response = requests.post('http://api.example.com/upload', files=files, data=data)
print(response.status_code)
Отправка нескольких файлов:
files = [
('files', open('file1.txt', 'rb')),
('files', open('file2.txt', 'rb')),
('files', open('file3.txt', 'rb')),
]
response = requests.post('http://api.example.com/upload-multiple', files=files)
2. Backend на FastAPI (приём файлов)
Базовый пример:
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post('/upload')
async def upload_file(file: UploadFile = File(...)):
contents = await file.read()
with open(f'uploads/{file.filename}', 'wb') as f:
f.write(contents)
return {
'filename': file.filename,
'size': len(contents),
'content_type': file.content_type
}
Валидация и обработка:
from fastapi import FastAPI, File, UploadFile, HTTPException
import os
from pathlib import Path
app = FastAPI()
UPLOAD_DIR = Path('uploads')
ALLOWED_EXTENSIONS = {'pdf', 'jpg', 'jpeg', 'png'}
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10 MB
@app.post('/upload')
async def upload_file(file: UploadFile = File(...)):
file_ext = file.filename.split('.')[-1].lower()
if file_ext not in ALLOWED_EXTENSIONS:
raise HTTPException(status_code=400, detail='Invalid file type')
contents = await file.read()
if len(contents) > MAX_FILE_SIZE:
raise HTTPException(status_code=413, detail='File too large')
import uuid
safe_name = f'{uuid.uuid4()}_{file.filename}'
upload_path = UPLOAD_DIR / safe_name
upload_path.parent.mkdir(parents=True, exist_ok=True)
with open(upload_path, 'wb') as f:
f.write(contents)
return {
'filename': safe_name,
'original_name': file.filename,
'size': len(contents)
}
3. Отправка нескольких файлов
FastAPI backend:
from typing import List
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post('/upload-multiple')
async def upload_files(files: List[UploadFile] = File(...)):
results = []
for file in files:
contents = await file.read()
results.append({
'filename': file.filename,
'size': len(contents)
})
return {'uploaded': results}
4. Отправка файла с дополнительными данными
FastAPI:
from fastapi import FastAPI, File, UploadFile, Form
app = FastAPI()
@app.post('/upload-with-metadata')
async def upload_with_metadata(
file: UploadFile = File(...),
user_id: int = Form(...),
title: str = Form(...)
):
contents = await file.read()
db.files.insert({
'user_id': user_id,
'title': title,
'filename': file.filename,
'size': len(contents),
'content_type': file.content_type
})
return {'status': 'success', 'message': 'File uploaded'}
Клиент:
with open('document.pdf', 'rb') as f:
files = {'file': f}
data = {'user_id': 42, 'title': 'Important Document'}
response = requests.post(
'http://api.example.com/upload-with-metadata',
files=files,
data=data
)
print(response.json())
5. Потоковая обработка больших файлов
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post('/upload-stream')
async def upload_stream(file: UploadFile = File(...)):
chunk_size = 1024 * 1024
total_size = 0
async with aiofiles.open(f'uploads/{file.filename}', 'wb') as f:
while True:
chunk = await file.read(chunk_size)
if not chunk:
break
await f.write(chunk)
total_size += len(chunk)
return {'filename': file.filename, 'total_size': total_size}
6. Использование curl для тестирования
curl -F 'file=@/path/to/file.pdf' http://api.example.com/upload
curl -F 'file=@photo.jpg' -F 'user_id=123' http://api.example.com/upload
curl -F 'files=@file1.txt' -F 'files=@file2.txt' http://api.example.com/upload-multiple
7. Обработка ошибок и безопасность
from fastapi import FastAPI, File, UploadFile, HTTPException
from pathlib import Path
app = FastAPI()
@app.post('/secure-upload')
async def secure_upload(file: UploadFile = File(...)):
allowed_mimes = {'application/pdf', 'image/jpeg', 'image/png'}
if file.content_type not in allowed_mimes:
raise HTTPException(status_code=400, detail='Invalid MIME type')
allowed_ext = {'.pdf', '.jpg', '.jpeg', '.png'}
file_ext = Path(file.filename).suffix.lower()
if file_ext not in allowed_ext:
raise HTTPException(status_code=400, detail='Invalid extension')
try:
contents = await file.read()
if file_ext == '.jpg' and not contents.startswith(b'\xff\xd8\xff'):
raise HTTPException(status_code=400, detail='Invalid JPEG')
with open(f'uploads/{file.filename}', 'wb') as f:
f.write(contents)
return {'status': 'success'}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Итоги
- multipart/form-data — стандарт для передачи файлов
- UploadFile в FastAPI — удобная абстракция
- Валидация обязательна: размер, расширение, MIME-тип, сигнатура
- Безопасные имена файлов: используйте UUID или хеши
- Потоковая обработка для больших файлов
- Обработка ошибок критична