Files
quoter/docs/upload-client-guide.md

474 lines
13 KiB
Markdown
Raw Normal View History

2025-09-03 10:21:17 +03:00
# 📤 Upload Client Guide - Quoter API
**Версия**: 0.6.2
**Дата**: 2025-01-28
**Статус**: 🚀 Production Ready
## 🎯 Обзор
Quoter предоставляет простой и надежный API для загрузки файлов с поддержкой:
- JWT аутентификации
- Квот пользователей (12 ГБ на пользователя)
- Множественных файлов в одном запросе
- Автоматического определения MIME типов
- Streaming загрузки с проверкой квот
## 🔗 Endpoints
### Base URL
```
Production: https://files.dscrs.site
Development: http://localhost:8080
```
### API Endpoints
| Method | Endpoint | Описание |
|--------|----------|----------|
| `GET` | `/` | Информация о пользователе и квоте |
| `POST` | `/` | Загрузка файлов |
| `GET` | `/{filename}` | Получение файла |
## 🔐 Аутентификация
POST запросы требуют JWT токен в заголовке `Authorization`:
```http
Authorization: Bearer <your-jwt-token>
```
### Формат токена
- JWT токен с claims: `{ user_id, username, exp?, iat? }`
- Минимальная длина: 100 символов
- Максимальная длина: 2048 символов
## 📊 Информация о пользователе
### GET /
Получает информацию о текущем пользователе и его квоте.
**Заголовки:**
```http
Authorization: Bearer <jwt-token>
```
**Ответ:**
```json
{
"user_id": "user123",
"username": "john_doe",
"token_type": "Bearer",
"created_at": "2025-01-28T10:00:00Z",
"last_activity": "2025-01-28T12:00:00Z",
"auth_data": {...},
"device_info": {...},
"quota": {
"current_quota": 1073741824,
"max_quota": 12884901888,
"usage_percentage": 8.33
}
}
```
**Коды ответов:**
- `200` - Успешно
- `401` - Неверный или истекший токен
## 📤 Загрузка файлов
### POST /
Загружает один или несколько файлов.
**Заголовки:**
```http
Authorization: Bearer <jwt-token>
Content-Type: multipart/form-data
```
**Параметры:**
- `file` (multipart) - Файл для загрузки (можно несколько)
**Лимиты:**
- Максимальный размер одного файла: **500 МБ**
- Максимальная квота пользователя: **12 ГБ**
- Поддерживаемые форматы: изображения, аудио, видео, документы
**Ответ (один файл):**
```
filename-uuid.ext
```
**Ответ (несколько файлов):**
```json
{
"uploaded_files": [
"file1-uuid.ext",
"file2-uuid.ext"
],
"count": 2
}
```
**Коды ответов:**
- `200` - Файл(ы) успешно загружены
- `400` - Нет файлов или все файлы пустые
- `401` - Неверный токен авторизации
- `413` - Превышен лимит размера файла или квоты
- `415` - Неподдерживаемый формат файла
- `500` - Ошибка сервера
## 📁 Получение файлов
### GET /{filename}
Получает загруженный файл.
**Параметры:**
- `filename` - Имя файла (UUID + расширение)
**Заголовки (опционально):**
```http
If-None-Match: "etag-value"
Range: bytes=0-1023
```
**Ответ:**
- `200` - Файл найден
- `304` - Файл не изменился (если передан ETag)
- `404` - Файл не найден
- `206` - Частичный контент (если передан Range)
## 💻 Примеры кода
### JavaScript/TypeScript
```typescript
class QuoterUploadClient {
private baseUrl: string;
private token: string;
constructor(baseUrl: string, token: string) {
this.baseUrl = baseUrl;
this.token = token;
}
// Получение информации о пользователе
async getUserInfo() {
const response = await fetch(`${this.baseUrl}/`, {
headers: {
'Authorization': `Bearer ${this.token}`
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
// Загрузка одного файла
async uploadFile(file: File): Promise<string> {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${this.baseUrl}/`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`
},
body: formData
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Upload failed: ${response.status} ${errorText}`);
}
return await response.text();
}
// Загрузка нескольких файлов
async uploadFiles(files: File[]): Promise<{uploaded_files: string[], count: number}> {
const formData = new FormData();
files.forEach(file => formData.append('file', file));
const response = await fetch(`${this.baseUrl}/`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`
},
body: formData
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Upload failed: ${response.status} ${errorText}`);
}
return await response.json();
}
// Получение файла
async getFile(filename: string): Promise<Blob> {
const response = await fetch(`${this.baseUrl}/${filename}`, {
headers: {
'Authorization': `Bearer ${this.token}`
}
});
if (!response.ok) {
throw new Error(`File not found: ${response.status}`);
}
return await response.blob();
}
}
// Использование
const client = new QuoterUploadClient('https://files.dscrs.site', 'your-jwt-token');
// Получение информации о пользователе
const userInfo = await client.getUserInfo();
console.log(`Квота: ${userInfo.quota.usage_percentage.toFixed(1)}%`);
// Загрузка файла
const fileInput = document.getElementById('fileInput') as HTMLInputElement;
const file = fileInput.files[0];
const filename = await client.uploadFile(file);
console.log(`Файл загружен: ${filename}`);
```
### Python
```python
import requests
import json
from typing import List, Dict, Any
class QuoterUploadClient:
def __init__(self, base_url: str, token: str):
self.base_url = base_url.rstrip('/')
self.token = token
self.headers = {'Authorization': f'Bearer {token}'}
def get_user_info(self) -> Dict[str, Any]:
"""Получение информации о пользователе"""
response = requests.get(f'{self.base_url}/', headers=self.headers)
response.raise_for_status()
return response.json()
def upload_file(self, file_path: str) -> str:
"""Загрузка одного файла"""
with open(file_path, 'rb') as f:
files = {'file': f}
response = requests.post(
f'{self.base_url}/',
headers=self.headers,
files=files
)
response.raise_for_status()
return response.text.strip()
def upload_files(self, file_paths: List[str]) -> Dict[str, Any]:
"""Загрузка нескольких файлов"""
files = []
for path in file_paths:
files.append(('file', open(path, 'rb')))
try:
response = requests.post(
f'{self.base_url}/',
headers=self.headers,
files=files
)
response.raise_for_status()
return response.json()
finally:
# Закрываем все файлы
for _, file_obj in files:
file_obj.close()
def get_file(self, filename: str, save_path: str = None) -> bytes:
"""Получение файла"""
response = requests.get(
f'{self.base_url}/{filename}',
headers=self.headers
)
response.raise_for_status()
if save_path:
with open(save_path, 'wb') as f:
f.write(response.content)
return response.content
# Использование
client = QuoterUploadClient('https://files.dscrs.site', 'your-jwt-token')
# Получение информации о пользователе
user_info = client.get_user_info()
print(f"Квота: {user_info['quota']['usage_percentage']:.1f}%")
# Загрузка файла
filename = client.upload_file('/path/to/file.jpg')
print(f"Файл загружен: {filename}")
# Получение файла
file_content = client.get_file(filename, '/path/to/downloaded_file.jpg')
```
### cURL
```bash
# Получение информации о пользователе
curl -H "Authorization: Bearer your-jwt-token" \
https://files.dscrs.site/
# Загрузка одного файла
curl -X POST \
-H "Authorization: Bearer your-jwt-token" \
-F "file=@/path/to/file.jpg" \
https://files.dscrs.site/
# Загрузка нескольких файлов
curl -X POST \
-H "Authorization: Bearer your-jwt-token" \
-F "file=@/path/to/file1.jpg" \
-F "file=@/path/to/file2.mp3" \
https://files.dscrs.site/
# Получение файла
curl -H "Authorization: Bearer your-jwt-token" \
-o downloaded_file.jpg \
https://files.dscrs.site/filename-uuid.jpg
```
## 🚨 Обработка ошибок
### Типичные ошибки
| Код | Описание | Решение |
|-----|----------|---------|
| `401` | Неверный токен | Проверить валидность JWT токена |
| `413` | Превышена квота | Удалить старые файлы или увеличить квоту |
| `413` | Файл слишком большой | Разделить файл на части |
| `415` | Неподдерживаемый формат | Проверить MIME тип файла |
| `500` | Ошибка сервера | Повторить запрос позже |
### Пример обработки ошибок
```typescript
async function uploadWithRetry(client: QuoterUploadClient, file: File, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client.uploadFile(file);
} catch (error) {
if (error.message.includes('413')) {
throw new Error('Файл слишком большой или квота превышена');
}
if (error.message.includes('401')) {
throw new Error('Токен авторизации недействителен');
}
if (attempt === maxRetries) {
throw error;
}
// Ждем перед повторной попыткой
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
```
## 📋 Поддерживаемые форматы
### Изображения
- JPEG, PNG, GIF, WebP, HEIC, BMP, TIFF
### Аудио
- MP3, WAV, AAC, M4A, OGG, FLAC
### Видео
- MP4, AVI, MOV, WebM, MKV
### Документы
- PDF, DOC, DOCX, TXT, RTF
## 🔧 Конфигурация
### Environment Variables
```bash
# Обязательные
JWT_SECRET_KEY=your-jwt-secret-key
2025-09-03 10:21:17 +03:00
REDIS_URL=redis://localhost:6379
STORJ_ACCESS_KEY=your-storj-access-key
STORJ_SECRET_KEY=your-storj-secret-key
STORJ_BUCKET=your-bucket-name
# Опциональные
PORT=8080
CORS_DOWNLOAD_ORIGINS=https://discours.io,https://*.discours.io
```
### Лимиты
```rust
// Максимальный размер одного файла
const MAX_SINGLE_FILE_BYTES: u64 = 500 * 1024 * 1024; // 500 МБ
// Максимальная квота пользователя
const MAX_USER_QUOTA_BYTES: u64 = 12 * 1024 * 1024 * 1024; // 12 ГБ
```
## 🧪 Тестирование
### Проверка подключения
```bash
# Проверка доступности API
curl -I https://files.dscrs.site/
# Проверка аутентификации
curl -H "Authorization: Bearer your-token" \
https://files.dscrs.site/
```
### Тестовые файлы
```bash
# Создание тестового файла
echo "Test content" > test.txt
# Загрузка тестового файла
curl -X POST \
-H "Authorization: Bearer your-token" \
-F "file=@test.txt" \
https://files.dscrs.site/
```
## 📚 Дополнительные ресурсы
- [API Reference](README.md)
- [Setup Guide](SETUP.md)
- [Features Overview](features.md)
- [CORS Configuration](architecture.md)
## 🆘 Поддержка
При возникновении проблем:
1. Проверьте валидность JWT токена
2. Убедитесь в правильности Content-Type
3. Проверьте размер файла и квоту пользователя
4. Изучите логи сервера для детальной диагностики
---
**💡 Совет**: Используйте streaming загрузку для больших файлов и всегда проверяйте квоту пользователя перед загрузкой.