1
0
forked from erp-dev/erp
Files
erpnew/docs/upload.md

426 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 文件上传接口文档
## 概述
通用文件上传接口,用于上传无法归类到具体业务的文件。
**基础路径**: `/api/v1/upload/`
**认证要求**: 所有接口都需要 JWT Token 认证
**内容格式**: `multipart/form-data` (上传时) / `application/json` (响应)
**注意事项**:
- 不支持列表查询list
- 不支持修改操作PUT/PATCH
- 仅支持单个文件查询、上传、删除操作
---
## 数据模型
### UploadedFile
| 字段 | 类型 | 说明 |
|------|------|------|
| id | integer | 文件ID |
| path | string | 文件存储路径(随机文件名) |
| file_url | string | 文件访问URL |
| owner | integer | 上传者用户ID |
| owner_username | string | 上传者用户名 |
| is_deleted | boolean | 是否已删除(软删除标记) |
| original_filename | string | 原始文件名 |
| file_size | integer | 文件大小(字节) |
| content_type | string | MIME类型如 image/jpeg |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
---
## 接口列表
### 1. 上传文件
**请求**
```
POST /api/v1/upload/
Content-Type: multipart/form-data
Authorization: Bearer <token>
```
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| file | file | 是 | 要上传的文件最大100MB |
**请求示例**
```bash
curl -X POST http://localhost:8000/api/v1/upload/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@/path/to/your/file.pdf"
```
**成功响应** (201 Created)
```json
{
"id": 1,
"path": "uploads/2025/11/17/a1b2c3d4e5f6...hex.pdf",
"file_url": "/media/uploads/2025/11/17/a1b2c3d4e5f6...hex.pdf",
"owner": 1,
"owner_username": "admin",
"is_deleted": false,
"original_filename": "document.pdf",
"file_size": 1048576,
"content_type": "application/pdf",
"created_at": "2025-11-17T10:30:00Z",
"updated_at": "2025-11-17T10:30:00Z"
}
```
**错误响应** (400 Bad Request)
```json
{
"file": ["未上传文件"]
}
```
```json
{
"file": ["文件大小不能超过100MB"]
}
```
**安全特性**:
- 文件名使用 UUID 随机化,防止文件名冲突和路径遍历攻击
- 原始文件名保存在数据库中,不影响存储安全
- 自动记录上传者信息
---
### 2. 获取文件信息
**请求**
```
GET /api/v1/upload/{id}/
Authorization: Bearer <token>
```
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | integer | 是 | 文件ID |
**请求示例**
```bash
curl -X GET http://localhost:8000/api/v1/upload/1/ \
-H "Authorization: Bearer YOUR_TOKEN"
```
**成功响应** (200 OK)
```json
{
"id": 1,
"path": "uploads/2025/11/17/a1b2c3d4e5f6...hex.pdf",
"file_url": "/media/uploads/2025/11/17/a1b2c3d4e5f6...hex.pdf",
"owner": 1,
"owner_username": "admin",
"is_deleted": false,
"original_filename": "document.pdf",
"file_size": 1048576,
"content_type": "application/pdf",
"created_at": "2025-11-17T10:30:00Z",
"updated_at": "2025-11-17T10:30:00Z"
}
```
**错误响应** (404 Not Found)
```json
{
"detail": "未找到"
}
```
**注意**: 已软删除的文件无法通过此接口查询
---
### 3. 软删除文件
**请求**
```
DELETE /api/v1/upload/{id}/
Authorization: Bearer <token>
```
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | integer | 是 | 文件ID |
**请求示例**
```bash
curl -X DELETE http://localhost:8000/api/v1/upload/1/ \
-H "Authorization: Bearer YOUR_TOKEN"
```
**成功响应** (200 OK)
```json
{
"detail": "文件已标记为删除"
}
```
**说明**:
- 软删除不会物理删除文件,只是标记为已删除
- 软删除后的文件无法通过常规接口查询
- 可以通过恢复接口恢复文件
---
### 4. 恢复已删除文件
**请求**
```
POST /api/v1/upload/{id}/restore/
Authorization: Bearer <token>
```
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | integer | 是 | 文件ID |
**请求示例**
```bash
curl -X POST http://localhost:8000/api/v1/upload/1/restore/ \
-H "Authorization: Bearer YOUR_TOKEN"
```
**成功响应** (200 OK)
```json
{
"detail": "文件已恢复",
"data": {
"id": 1,
"path": "uploads/2025/11/17/a1b2c3d4e5f6...hex.pdf",
"file_url": "/media/uploads/2025/11/17/a1b2c3d4e5f6...hex.pdf",
"owner": 1,
"owner_username": "admin",
"is_deleted": false,
"original_filename": "document.pdf",
"file_size": 1048576,
"content_type": "application/pdf",
"created_at": "2025-11-17T10:30:00Z",
"updated_at": "2025-11-17T10:30:00Z"
}
}
```
**错误响应** (400 Bad Request)
```json
{
"detail": "文件未被删除,无需恢复"
}
```
---
### 5. 永久删除文件
**请求**
```
DELETE /api/v1/upload/{id}/permanent_delete/
Authorization: Bearer <token>
```
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | integer | 是 | 文件ID |
**请求示例**
```bash
curl -X DELETE http://localhost:8000/api/v1/upload/1/permanent_delete/ \
-H "Authorization: Bearer YOUR_TOKEN"
```
**成功响应** (204 No Content)
```json
{
"detail": "文件已永久删除"
}
```
**说明**:
- 永久删除会物理删除文件和数据库记录
- 此操作不可恢复,请谨慎使用
- 建议仅在确认不需要时使用
---
## 使用示例
### Python (requests)
```python
import requests
# 配置
BASE_URL = "http://localhost:8000/api/v1"
TOKEN = "your_jwt_token"
headers = {"Authorization": f"Bearer {TOKEN}"}
# 1. 上传文件
with open('document.pdf', 'rb') as f:
files = {'file': f}
response = requests.post(
f"{BASE_URL}/upload/",
headers=headers,
files=files
)
file_data = response.json()
file_id = file_data['id']
print(f"上传成功文件ID: {file_id}")
# 2. 获取文件信息
response = requests.get(
f"{BASE_URL}/upload/{file_id}/",
headers=headers
)
print(f"文件信息: {response.json()}")
# 3. 软删除文件
response = requests.delete(
f"{BASE_URL}/upload/{file_id}/",
headers=headers
)
print(f"软删除: {response.json()}")
# 4. 恢复文件
response = requests.post(
f"{BASE_URL}/upload/{file_id}/restore/",
headers=headers
)
print(f"恢复文件: {response.json()}")
# 5. 永久删除
response = requests.delete(
f"{BASE_URL}/upload/{file_id}/permanent_delete/",
headers=headers
)
print(f"永久删除完成")
```
### JavaScript (Axios)
```javascript
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const BASE_URL = 'http://localhost:8000/api/v1';
const TOKEN = 'your_jwt_token';
const headers = { Authorization: `Bearer ${TOKEN}` };
// 1. 上传文件
async function uploadFile() {
const formData = new FormData();
formData.append('file', fs.createReadStream('document.pdf'));
const response = await axios.post(
`${BASE_URL}/upload/`,
formData,
{ headers: { ...headers, ...formData.getHeaders() } }
);
console.log('上传成功:', response.data);
return response.data.id;
}
// 2. 获取文件信息
async function getFileInfo(fileId) {
const response = await axios.get(
`${BASE_URL}/upload/${fileId}/`,
{ headers }
);
console.log('文件信息:', response.data);
}
// 3. 软删除
async function softDelete(fileId) {
const response = await axios.delete(
`${BASE_URL}/upload/${fileId}/`,
{ headers }
);
console.log('软删除:', response.data);
}
// 4. 恢复文件
async function restore(fileId) {
const response = await axios.post(
`${BASE_URL}/upload/${fileId}/restore/`,
{},
{ headers }
);
console.log('恢复:', response.data);
}
// 5. 永久删除
async function permanentDelete(fileId) {
const response = await axios.delete(
`${BASE_URL}/upload/${fileId}/permanent_delete/`,
{ headers }
);
console.log('永久删除完成');
}
```
---
## 错误码说明
| HTTP状态码 | 说明 |
|-----------|------|
| 200 | 成功 |
| 201 | 创建成功 |
| 204 | 删除成功(无内容) |
| 400 | 请求参数错误 |
| 401 | 未认证或认证失败 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 413 | 文件过大 |
| 500 | 服务器内部错误 |
---
## 最佳实践
1. **文件大小限制**: 单文件最大100MB超过此限制会返回400错误
2. **文件命名**: 系统自动使用UUID生成随机文件名原始文件名保存在`original_filename`字段
3. **软删除策略**: 建议先使用软删除,确认不需要后再使用永久删除
4. **文件访问**: 使用返回的`file_url`字段访问文件
5. **权限控制**: 所有接口都需要认证,上传的文件自动关联当前用户
---
## 注意事项
1. **不支持的操作**:
- ❌ 列表查询 (`GET /api/v1/upload/`)
- ❌ 批量上传
- ❌ 修改文件 (`PUT/PATCH /api/v1/upload/{id}/`)
2. **文件存储**:
- 文件按日期组织:`uploads/YYYY/MM/DD/`
- 文件名使用32位十六进制UUID
- 保留原始文件扩展名
3. **查询限制**:
- 默认查询会过滤掉已软删除的文件
- 要访问已删除文件需要通过Django Admin或直接数据库查询
4. **安全考虑**:
- 所有文件名随机化,防止路径遍历攻击
- 需要JWT认证
- 自动记录上传者信息