145 lines
3.5 KiB
Markdown
145 lines
3.5 KiB
Markdown
|
|
# 临时文件上传功能设计
|
|||
|
|
|
|||
|
|
**日期**: 2026-05-25
|
|||
|
|
**状态**: 已批准
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
在现有 `/public/` 公开内容页面中增加临时文件上传功能,允许任何人上传文件并自动在指定时间后删除。
|
|||
|
|
|
|||
|
|
## 功能需求
|
|||
|
|
|
|||
|
|
1. 上传临时文件作为 PublicContent 类型发布
|
|||
|
|
2. 上传者选择过期时间:1小时 / 1天 / 7天
|
|||
|
|
3. 上传文件最大500MB
|
|||
|
|
4. 过期文件自动从存储中删除
|
|||
|
|
5. 上传表单对人类用户视觉隐藏(1px宽度),但agent可访问
|
|||
|
|
6. 提供REST API方式上传文件
|
|||
|
|
|
|||
|
|
## 技术方案
|
|||
|
|
|
|||
|
|
### 1. 模型改动
|
|||
|
|
|
|||
|
|
**文件**: `core/models.py`
|
|||
|
|
|
|||
|
|
在 `PublicContent` 模型中增加字段:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
is_temp_file = models.BooleanField(default=False, verbose_name='临时文件')
|
|||
|
|
expire_type = models.CharField(
|
|||
|
|
max_length=10,
|
|||
|
|
choices=[
|
|||
|
|
('expire_1h', '1小时'),
|
|||
|
|
('expire_1d', '1天'),
|
|||
|
|
('expire_7d', '7天'),
|
|||
|
|
],
|
|||
|
|
blank=True, null=True,
|
|||
|
|
verbose_name='过期类型'
|
|||
|
|
)
|
|||
|
|
expire_at = models.DateTimeField(blank=True, null=True, verbose_name='过期时间')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 表单改动
|
|||
|
|
|
|||
|
|
**文件**: `core/forms.py`
|
|||
|
|
|
|||
|
|
扩展 `PublicContentForm`,增加过期类型选择字段。
|
|||
|
|
|
|||
|
|
### 3. 上传表单HTML
|
|||
|
|
|
|||
|
|
**文件**: `core/templates/core/public_content.html`
|
|||
|
|
|
|||
|
|
在页面底部添加上传表单,设置 `style="width: 1px"` 使人类用户难以阅读,但agent可解析。
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<form method="post" enctype="multipart/form-data" style="width: 1px; position: absolute; left: -9999px;" action="{% url 'temp_upload' %}">
|
|||
|
|
{% csrf_token %}
|
|||
|
|
{{ temp_upload_form }}
|
|||
|
|
<button type="submit">上传</button>
|
|||
|
|
</form>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. API端点
|
|||
|
|
|
|||
|
|
**文件**: `core/urls.py` 和 `core/views.py`
|
|||
|
|
|
|||
|
|
新增 API 端点 `POST /api/v1/temp-upload/`:
|
|||
|
|
|
|||
|
|
- 路径: `api/v1/temp-upload/`
|
|||
|
|
- 方法: POST
|
|||
|
|
- 参数:
|
|||
|
|
- `file`: 文件 (必填, 最大500MB)
|
|||
|
|
- `title`: 文件标题 (必填)
|
|||
|
|
- `expire_type`: 过期类型 `expire_1h` | `expire_1d` | `expire_7d` (必填)
|
|||
|
|
- 返回: JSON `{ "success": true, "file_url": "...", "expire_at": "..." }`
|
|||
|
|
|
|||
|
|
### 5. 定时清理任务
|
|||
|
|
|
|||
|
|
**文件**: `core/tasks.py`
|
|||
|
|
|
|||
|
|
新增 Celery 任务 `cleanup_expired_temp_files`:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@shared_task
|
|||
|
|
def cleanup_expired_temp_files():
|
|||
|
|
expired_files = PublicContent.objects.filter(
|
|||
|
|
is_temp_file=True,
|
|||
|
|
expire_at__lte=timezone.now()
|
|||
|
|
)
|
|||
|
|
for file in expired_files:
|
|||
|
|
# 删除物理文件
|
|||
|
|
if file.file:
|
|||
|
|
file.file.delete()
|
|||
|
|
file.delete()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
配置 Celery Beat 每小时执行。
|
|||
|
|
|
|||
|
|
### 6. API文档(写在公开内容页面)
|
|||
|
|
|
|||
|
|
```markdown
|
|||
|
|
## 临时文件上传 API
|
|||
|
|
|
|||
|
|
### 上传文件
|
|||
|
|
POST /api/v1/temp-upload/
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
- `file`: 文件 (multipart/form-data, 最大500MB)
|
|||
|
|
- `title`: 文件标题 (string)
|
|||
|
|
- `expire_type`: 过期时间 (`expire_1h` | `expire_1d` | `expire_7d`)
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"success": true,
|
|||
|
|
"file_url": "/media/temp_files/xxx.pdf",
|
|||
|
|
"expire_at": "2026-05-25T18:30:00Z",
|
|||
|
|
"file_name": "document.pdf",
|
|||
|
|
"file_size": 1048576
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**示例**:
|
|||
|
|
```bash
|
|||
|
|
curl -X POST -F "file=@document.pdf" -F "title=测试文件" -F "expire_type=expire_1d" http://localhost:8000/api/v1/temp-upload/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 数据库迁移
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python manage.py makemigrations core --name add_temp_file_fields
|
|||
|
|
python manage.py migrate
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Celery Beat 配置
|
|||
|
|
|
|||
|
|
在 `diary_family/celery.py` 中添加周期任务:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
CELERY_BEAT_SCHEDULE = {
|
|||
|
|
'cleanup-expired-temp-files': {
|
|||
|
|
'task': 'core.tasks.cleanup_expired_temp_files',
|
|||
|
|
'schedule': crontab(minute=0), # 每小时整点执行
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|