← Назад к вопросам

Как используя 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 или хеши
  • Потоковая обработка для больших файлов
  • Обработка ошибок критична