CI 测试 / go vet + go test (push) Failing after 5s
- 对象存储直链:S3 引擎 302 到限时预签名 URL(有效期钳位分享剩余时效),失败自动回落代理 - 过期回收:janitor 定时扫描 + 取件惰性回收 + 管理端手动触发(POST /admin/recycle/run), retention_days 最长存储时长;删除走引用计数(去重对象安全) - SHA512 内容去重:三条上传链路落库后计算哈希,命中即复用旧对象并删除本次副本 - 下载防盗链:Referer 白名单(同源/空 Referer/通配域名放行),挂 /share/download - 取件页图片/音频内联预览(下载地址直连,加载失败回退下载按钮) - 文件夹上传:拖拽目录明确提示"建议压缩后上传"(webkitGetAsEntry 探测) - 管理端设置卡「回收与下载安全」8 个新配置键(KVSchema + configKeys + UI + i18n) - 前端产物重建并同步 server/web/dist 与 web-embed - 文档:10-config 配置表、03-file-share 直链/防盗链/文件夹章节、07-admin 回收端点、openapi
132 lines
4.5 KiB
Markdown
132 lines
4.5 KiB
Markdown
# 文件分享
|
||
|
||
上传单个文件并创建分享。支持扩展名/MIME 白名单 + **magic bytes 防伪**(读文件前 64 字节校验,
|
||
伪造类型返回 403)。经审计中间件落库(action=upload,记录文件总大小与实际传输字节)。
|
||
|
||
## POST /share/file
|
||
|
||
**请求参数**(`multipart/form-data`):
|
||
|
||
| 参数 | 类型 | 必需 | 默认 | 说明 |
|
||
|---|---|---|---|---|
|
||
| `code` | string,可选;自定义提取码,5-8 位字母或数字(空=随机生成;占用 400) |
|
||
| `file` | file | ✅ | - | 上传的文件(大小 ≤ 生效上限:`max_file_size>0` 时为其,否则 `uploadSize`) |
|
||
| `expire_value` | int | ❌ | `1` | 过期值(配合 `expire_style`;`count` 型受 `max_save_count` 约束) |
|
||
| `expire_style` | string | ❌ | `day` | `day`/`hour`/`minute`/`count`/`forever`(须在 `expireStyle` 白名单内) |
|
||
|
||
**curl 示例**:
|
||
|
||
```bash
|
||
curl -s -X POST http://localhost:8466/share/file \
|
||
-F 'file=@./report.pdf;type=application/pdf' \
|
||
-F 'expire_value=7' \
|
||
-F 'expire_style=day'
|
||
```
|
||
|
||
**成功响应**(200):
|
||
|
||
```json
|
||
{ "code": 200, "msg": "ok", "data": { "code": "K3P9W", "name": "report.pdf" } }
|
||
```
|
||
|
||
**错误响应**:
|
||
|
||
```json
|
||
{ "code": 400, "msg": "缺少上传文件 file 字段" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" }
|
||
```
|
||
|
||
> 大小上限为动态策略(26.9 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后
|
||
> **下一次上传立即按新上限执行**,无需重启;上限值可经 `GET /api/v1/config` 的
|
||
> `max_file_size`/`maxFileSize` 字段读取。
|
||
|
||
```json
|
||
{ "code": 403, "msg": "不允许上传该类型文件" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 403, "msg": "文件内容与扩展名不匹配,疑似伪造类型" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 403, "msg": "限制最长时间为 7天,可换用其他方式" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 403, "msg": "限制次数最多为 5 次" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 403, "msg": "请求次数过多,请稍后再试" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 507, "msg": "存储空间已达到管理员设置的容量上限" }
|
||
```
|
||
|
||
```json
|
||
{ "code": 503, "msg": "存储服务不可用,请稍后再试" }
|
||
```
|
||
|
||
## 文件类型白名单
|
||
|
||
由配置 `allowed_file_types` 控制(管理端可改):
|
||
|
||
- `*`:不限制(默认)。
|
||
- 扩展名规则:`.png`、`pdf`(自动补点)等,按文件名后缀匹配。
|
||
- MIME 规则:`image/*`、`application/pdf` 等,按请求 `Content-Type` 通配匹配。
|
||
|
||
已知类型(png/jpg/gif/webp/bmp/pdf/zip/rar/7z/gz/mp3/mp4/exe/elf)会做 **magic bytes 交叉校验**:
|
||
扩展名或 Content-Type 声明了已知类型,但文件头不匹配时拒绝(403「疑似伪造类型」)。
|
||
|
||
## 下载取件
|
||
|
||
- `GET /share/select?code=<code>`:消耗 1 次取件,返回文件流(`200` 全量 / `206` 区间,
|
||
支持 `Range` 请求头;响应含 `Accept-Ranges: bytes`、`Content-Disposition: attachment; filename*=UTF-8''...`)。
|
||
- `POST /share/select`:返回详情 JSON,`download_url` 为代理下载地址(见下)。
|
||
- `GET /share/download?key=<token>&code=<code>`:代理下载,消耗 1 次,同样支持 Range。
|
||
|
||
Range 示例(取前 1024 字节):
|
||
|
||
```bash
|
||
curl -s -H 'Range: bytes=0-1023' -o part.bin \
|
||
"http://localhost:8466/share/select?code=K3P9W"
|
||
```
|
||
|
||
区间越界返回:
|
||
|
||
```json
|
||
{ "code": 416, "msg": "请求范围超出文件大小" }
|
||
```
|
||
|
||
## 直链下载(26.9)
|
||
|
||
存储引擎为对象存储(S3)且 `direct_download=1` 时,`GET /share/select` 与
|
||
`GET /share/download` 不再代理文件流,而是 `302` 重定向到限时预签名 URL——
|
||
文件字节不经过本服务器,带宽成本转嫁对象存储。
|
||
|
||
- 签名有效期 = `direct_link_expire`(默认 900 秒)与分享剩余时效的较小值;
|
||
- 引擎不支持直链(如 local/WebDAV)时自动回落代理下载,取件不中断;
|
||
- 审计照常记录(`transferred_bytes` 记为文件大小)。
|
||
|
||
## 下载防盗链(26.9)
|
||
|
||
`hotlink_enabled=1` 时,`/share/download` 校验 `Referer`:
|
||
|
||
| Referer | 行为 |
|
||
|---|---|
|
||
| 空(直接访问 / curl / 地址栏) | 放行 |
|
||
| 与请求 Host 同源 | 放行 |
|
||
| 命中 `hotlink_whitelist`(逗号分隔域名,支持 `*.example.com` 通配) | 放行 |
|
||
| 其余 | `403`(JSON 错误体) |
|
||
|
||
白名单为空时仅同源放行。开关与白名单均为管理端 KV,修改后立即生效。
|
||
|
||
## 文件夹上传(26.9)
|
||
|
||
不支持文件夹上传(前端已移除目录选择;拖拽目录会提示"建议压缩后上传")。
|
||
后端 `SanitizeFileName` 会剥离文件名中的路径分隔符,多级路径无法成体保存。
|