Files
UploaderHui/NAS图片上传系统_开发文档.md
2026-04-18 15:08:57 +08:00

383 lines
11 KiB
Markdown
Raw 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.
# NAS图片上传系统 - 开发文档
> **版本**v0.3
> **日期**2026-04-17
> **状态**:需求已确认,待开发
---
## 1. 项目概述
### 1.1 项目背景
需要搭建一个简单的Web应用允许用户通过手机或电脑访问网站将图片上传到本地NAS存储中。网站部署在公网实例上通过内网穿透转发请求到本地NAS。
### 1.2 核心需求
- 用户通过手机/电脑浏览器访问网站,选择目标文件夹后上传图片
- 支持多选图片上传,带上传进度条显示
- 上传完成后显示成功上传的图片列表,方便用户确认是否全部上传成功
- 文件夹结构训练营N期 → 25个班级文件夹用户只能选择不能创建
- 文件夹自动创建:首次上传时若目标文件夹不存在则自动创建
- 每个训练营文件夹下有一个MD文档统计图片数量
- 无需登录,公开使用(后续考虑加密码保护)
- 仅上传功能,不提供图片浏览/下载
- 图片主要为屏幕截图
### 1.3 网络架构
公网实例通过内网穿透frp将请求转发到本地NAS上的服务。服务端直接运行在群晖NAS上。
```
用户手机/电脑 → 公网实例frp → 内网穿透 → NAS服务端
```
---
## 2. 技术方案
### 2.1 技术栈
| 层次 | 技术选型 | 说明 |
|------|----------|------|
| 后端框架 | Python + Flask | 轻量、开发快、文件操作方便NAS环境通常支持Python |
| 前端 | HTML + CSS + JavaScript | 纯静态页面,无需前端构建工具,部署简单 |
| 图片上传 | XMLHttpRequest | 原生支持上传进度事件 |
| 文件存储 | NAS本地文件系统 | 直接读写NAS磁盘目录 |
| 内网穿透 | frp | 将公网请求转发到内网服务 |
| 配置管理 | .env 文件 | 部署时填写NAS路径、端口等配置 |
---
## 3. 系统架构
### 3.1 整体架构
```
┬── 用户手机/电脑(浏览器)
├── 公网实例frp server
├── frp client → 内网穿透
└── 群晖 NASFlask + 静态文件)
├── /api/folders 获取文件夹列表
├── /api/upload 上传图片
├── /static/ 前端静态资源
└── {NAS_DATA_PATH}/ NAS存储目录.env配置
```
### 3.2 NAS目录结构
```
{NAS_DATA_PATH}/ ← 通过 .env 配置
├── 训练营13期/
│ ├── 统计.md ← 图片数量统计文档
│ ├── 1班/
│ ├── 2班/
│ ├── ...
│ └── 25班/
├── 训练营14期/
│ ├── 统计.md
│ ├── 1班/
│ ├── ...
│ └── 25班/
└── ...
```
### 3.3 服务端项目结构
```
nas-uploader/
├── app.py ← Flask主应用
├── config.py ← 读取 .env 配置
├── .env ← 部署配置NAS路径、端口等
├── .env.example ← 配置示例文件
├── requirements.txt ← Python依赖
├── static/
│ ├── css/style.css ← 样式文件
│ ├── js/app.js ← 前端逻辑
│ └── img/ ← 图标等静态资源
├── templates/
│ └── index.html ← 主页面
└── start.sh ← 启动脚本
```
### 3.4 .env 配置文件
```env
# NAS图片存储根目录部署时修改为实际路径
NAS_DATA_PATH=/volume1/nas_data
# 服务端口
PORT=5000
# 允许的图片格式(小写,逗号分隔)
ALLOWED_EXTENSIONS=jpg,jpeg,png,gif,webp
# 单次上传最大文件数量
MAX_FILES_COUNT=100
# 单张图片最大大小MB
MAX_FILE_SIZE_MB=20
```
---
## 4. 功能设计
### 4.1 文件夹选择功能
#### 4.1.1 文件夹获取规则
- 后端扫描NAS根目录`NAS_DATA_PATH`),读取所有"训练营*期"格式的文件夹
- 每个训练营文件夹下,读取所有"*班"格式的子文件夹
- 前端以两级联动选择器展示:第一级选训练营,第二级选班级
- 文件夹列表按名称排序(数字自然排序)
- **不显示**各班级已有图片数量
#### 4.1.2 文件夹自动创建
- 当用户选择某个班级并上传图片时,若对应的文件夹不存在,则自动创建
- 创建逻辑:检查训练营文件夹 → 检查班级文件夹 → 不存在则创建
- 同时创建对应的统计.md文档若训练营文件夹是新建的
- **新的"训练营N期"文件夹由管理员在NAS上手动创建**,用户不能通过网页创建新训练营
### 4.2 图片上传功能
#### 4.2.1 上传流程
```
用户选择文件夹 → 点击"选择图片"按钮 → 多选图片 → 点击"开始上传"
```
- 支持的图片格式JPG、JPEG、PNG、GIF、WEBP不支持HEIC
- 单次上传数量限制最多100张
- 单张大小限制20MB
- 采用逐张上传策略,每张图片独立显示进度,同时显示总体进度
#### 4.2.2 图片命名规则
上传后图片按以下格式重命名:
```
训练营{n}期-{x}班-{时间戳}-{随机数}.{扩展名}
```
示例:
```
训练营13期-1班-20260417143022-a3f8b2.png
训练营13期-1班-20260417143022-7c2e91.jpg
```
- `{n}`:训练营期数
- `{x}`:班级编号
- `{时间戳}`:精确到秒,格式 `YYYYMMDDHHmmss`
- `{随机数}`6位十六进制随机字符串
- `{扩展名}`:保留原始文件的扩展名
- 若文件名已存在(极低概率),自动在末尾追加序号:`-a3f8b2-1.png`
#### 4.2.3 上传进度显示
- 总体进度条:显示已上传完成的数量 / 总数量
- 单张进度条:每张图片的上传百分比
- 状态标识:等待中 / 上传中 / 成功 / 失败
#### 4.2.4 上传完成反馈
- 上传成功后,页面底部显示"已成功上传"图片缩略图列表
- 每张图片显示:缩略图 + 新文件名 + 上传状态
- 失败的图片明确标红,显示失败原因,支持"重试"操作
- 显示摘要信息:"成功上传15张失败 1张"
### 4.3 图片统计功能
#### 4.3.1 统计.md格式
每个训练营文件夹下的统计.md内容格式如下
```markdown
# 训练营XX期 图片统计
更新时间2026-04-17 14:30:00
图片总数156
| 班级 | 图片数量 |
|------|----------|
| 1班 | 12 |
| 2班 | 8 |
| ... | ... |
| 25班 | 6 |
```
#### 4.3.2 统计更新触发
- 每次上传成功后,自动更新对应训练营的统计.md
- 统计方式:扫描训练营文件夹及其所有子目录,统计图片文件数量
- 图片文件判定根据文件扩展名jpg、jpeg、png、gif、webp
- 更新时间记录在文档头部
---
## 5. API设计
### 5.1 获取文件夹列表
**GET /api/folders**
返回所有训练营及其下的班级文件夹。
**响应示例:**
```json
{
"folders": [
{
"name": "训练营13期",
"classes": ["1班", "2班", "3班", "4班", "5班", "...", "25班"]
},
{
"name": "训练营14期",
"classes": ["1班", "3班"]
}
]
}
```
### 5.2 上传图片
**POST /api/upload**
接受multipart/form-data格式的图片上传。
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| folder | string | 是 | 训练营名称,如"训练营13期" |
| class | string | 是 | 班级名称,如"1班" |
| files | File[] | 是 | 图片文件数组,支持多文件 |
**响应示例:**
```json
{
"success": true,
"uploaded": [
{ "filename": "训练营13期-1班-20260417143022-a3f8b2.png", "status": "success" },
{ "filename": "训练营13期-1班-20260417143023-7c2e91.jpg", "status": "success" }
],
"failed": [
{ "filename": "photo3.bmp", "status": "failed", "reason": "文件格式不支持" }
]
}
```
---
## 6. 前端页面设计
### 6.1 页面布局
页面采用简洁的单页设计,适配手机和电脑访问(响应式布局)。
页面从上到下分为三个区域:
#### 6.1.1 顶部:文件夹选择区
- 第一行:下拉选择"训练营"(如"训练营13期"
- 第二行:下拉选择"班级"(如"1班"
- 班级列表根据选中的训练营动态加载
#### 6.1.2 中部:图片选择和上传区
- "选择图片"按钮:触发文件选择器,支持多选
- 已选择的图片列表:显示缩略图、文件名、可删除
- "开始上传"按钮:触发上传操作
- 进度条:总体进度 + 单张进度
#### 6.1.3 底部:上传结果区
- 显示成功上传的图片缩略图网格
- 失败的图片显示红色标记和"重试"按钮
- 显示摘要信息:"成功上传15张失败 1张"
### 6.2 移动端适配
- 采用响应式设计,自动适配手机和电脑屏幕
- 手机端优化:按钮大小适中触摸操作,图片缩略图网格自动调整列数
- 支持手机原生图片选择器accept="image/*"
---
## 7. 部署方案
### 7.1 frp内网穿透配置
详见 [frp内网穿透_原理与部署指南.md](./frp内网穿透_原理与部署指南.md)
**frpc.tomlNAS侧关键配置**
```toml
serverAddr = "你的公网IP"
serverPort = 7000
auth.token = "你的认证密码"
[[proxies]]
name = "nas-uploader"
type = "tcp"
localIP = "127.0.0.1"
localPort = 5000
remotePort = 8080
```
### 7.2 服务启动
```bash
#!/bin/bash
# start.sh
cd /path/to/nas-uploader
source venv/bin/activate # 可选:使用虚拟环境
python app.py
```
### 7.3 部署步骤
1. 将项目文件上传到群晖NAS
2. 复制 `.env.example``.env`,修改 `NAS_DATA_PATH` 为实际的共享文件夹路径
3. 安装Python依赖`pip install -r requirements.txt`
4. 启动服务:`bash start.sh`
5. 配置frpc客户端连接到公网服务器
6. 浏览器访问 `http://公网IP:8080` 验证
---
## 8. 开发计划
| 阶段 | 内容 | 预估工时 |
|------|------|----------|
| 第一阶段 | 后端基础Flask服务 + .env配置 + API + 文件操作 | 2-3小时 |
| 第二阶段 | 前端页面:文件夹选择 + 图片上传 + 进度条 + 结果展示 | 2-3小时 |
| 第三阶段 | 统计功能:自动更新统计.md | 1小时 |
| 第四阶段 | 优化:移动端适配 + 异常处理 + 测试 | 1-2小时 |
| 第五阶段 | 部署frp配置 + 群晖NAS部署 + 服务上线 | 0.5小时 |
---
## 9. 风险与注意事项
- **并发上传**:多用户同时上传到同一文件夹时,文件名含时间戳+随机数,冲突概率极低;若冲突则自动追加序号
- **存储空间**需关注NAS磁盘空间建议在上传前检查剩余空间
- **网络稳定性**:内网穿透可能因网络波动导致上传中断,需支持失败重试
- **安全性**:当前公开访问无需登录,后续考虑加密码保护;后端必须验证文件类型防止恶意上传
- **文件类型验证**后端严格校验文件扩展名和MIME类型防止上传非图片文件
---
## 10. 后续规划(暂不实现)
- 访问密码保护
- HTTPS 加密传输
- 图片浏览/下载功能
- 管理后台