轻量级 WebApp 部署复盘:WebUploadApp
入档:2026-06-07 项目:WebUploadApp 轻量级文件上传页 场景:腾讯云 Ubuntu 服务器,协作者通过网页上传图片 / 视频到固定目录 性质:项目复盘 + 可复用部署方法
事实记录(不可修改区)
- 预期目标:搭建一个小团队可用的文件上传网页,把图片 / 视频上传到服务器固定目录。
- 本地实现:Python + Flask,入口
app.py,模板templates/upload.html,测试 5 项通过。 - 服务器环境:腾讯云 Ubuntu,root Web 终端可登录,本机 SSH 接管未成功。
- 部署路径:应用目录
/opt/WebUploadApp,上传目录/data/webupload/uploads。 - 最终结果:网页可访问,Basic Auth 生效,上传成功,文件能落盘到固定目录。
- 数据来源:本次实操、服务器命令输出、页面实测反馈。
- 关键故障:
- Nginx 启动失败:80 端口被 Caddy 占用。
- 上传较大文件时报
ERR_CONNECTION_RESET:上传限制 / 超时配置不足。 - 进度条卡在 100%:浏览器上传完成,但前端整页替换响应处理不稳。
一句话
轻量级 Web 上传工具不是“Flask 页面能跑”就结束,真正的部署成败取决于四层同时对齐:上传目录、反向代理大小限制、后端超时、前端上传反馈。
本次工作流
- 本地先做最小 Flask 原型:文件类型白名单、大小限制、
secure_filename、短 UUID 防重名、测试覆盖。 - 服务器部署采用
Gunicorn -> Nginx -> Basic Auth,上传目录固定到/data/webupload/uploads。 - 因本机 SSH 卡住,改用腾讯云 Web 终端粘贴部署脚本,降低接管成本。
- Caddy 占用 80 端口后,确认服务器无其他站点依赖,再停用 Caddy,让 Nginx 接管。
- 首次上传成功后,按实际用户体验补进度条和大文件配置。
- 进度条卡 100% 后,把上传接口改为 JSON 返回,前端只更新结果区,不再
document.write()整页替换。
方法论沉淀
1. 部署先查端口占用,不要默认 Nginx 是唯一入口
核心:云服务器上 80 端口可能已被 Caddy、Apache、面板服务或手动进程占用;Nginx 配置正确不等于能启动。
来源:本次 nginx -t 成功,但 systemctl restart nginx 失败,日志显示 bind() to 0.0.0.0:80 failed,最终 ss -ltnp 查到 Caddy 占用 80。
验证状态:首次发现。
操作规则:
- Nginx 启动失败时先跑
ss -ltnp | grep ':80'。 - 如果是 Caddy / Apache,占用服务是否能停必须先判断,不要盲停生产站点。
nginx -t只证明语法正确,不证明端口可绑定。
边界:如果服务器已有正式网站,应改用现有 Caddy/Nginx 配置接入新应用,而不是停服务。
2. 大文件上传要同时改三层限制
核心:单改 Flask 的 MAX_CONTENT_LENGTH 不够;Nginx、Gunicorn、浏览器连接时间也会各自截断上传。
来源:上传视频时出现连接重置,后续通过放宽 client_max_body_size、Nginx 超时、Gunicorn --timeout、环境变量 UPLOAD_MAX_MB 后恢复。
验证状态:首次发现。
操作规则:
- Flask:用环境变量控制
UPLOAD_MAX_MB,避免写死 100MB。 - Nginx:同步设置
client_max_body_size、client_body_timeout、proxy_read_timeout。 - Gunicorn:设置
--timeout 900这类足够长的上传窗口。 - 修改后用小图和大视频各测一次,只测小图不能证明大文件链路可用。
边界:2GB 只是临时协作阈值;更大文件应考虑分片上传、对象存储或专用传输工具。
3. 上传进度 100% 不等于服务端保存完成
核心:XHR 的 upload progress 只表示浏览器把请求体发完,不代表 Flask 已保存完成、也不代表页面已收到业务成功响应。
来源:页面进度条到 100% 后一直停留。前端提示应从“正在上传”切到“服务器正在保存”,再等待后端业务响应。
验证状态:首次发现。
操作规则:
- 进度条到 100% 时显示“上传完成,服务器正在保存…”。
- 后端成功后再显示“上传成功”和保存文件名。
- 对大文件上传,必须区分“传输完成”和“保存确认”两个阶段。
边界:如果要显示服务端保存进度,需要更复杂的分片/任务状态机制;普通 Flask 单请求无法天然提供服务端保存百分比。
4. 上传接口返回 JSON,比整页 HTML 替换更稳
核心:有进度条的表单上传应让 /upload 返回 JSON,由前端局部更新结果;不要在 XHR 完成后 document.write(xhr.responseText)。
来源:初版进度条用 XHR 上传后整页写回 HTML,出现卡在 100% 的体验问题。改成 JSON 后,页面可稳定显示成功 / 错误结果。
验证状态:首次发现。
操作规则:
- 页面 GET
/返回 HTML。 - 上传 POST
/upload返回{ ok, filename }或{ ok:false, error }。 - 前端用
onload / onerror / ontimeout分别处理成功、连接中断和超时。 - 上传按钮在请求中禁用,请求结束后恢复。
边界:如果项目坚持无 JS 表单提交,则可继续返回 HTML;但一旦需要进度条,就应切换 JSON 接口。
5. Agent 不能直接 SSH 时,Web 终端粘贴脚本是有效降级方案
核心:远程接管失败不等于部署失败;把部署动作收敛成一段可复制脚本,可以绕开 SSH、SFTP、网页终端控制权等卡点。
来源:本机到服务器 SSH 端口可达,但 OpenSSH 登录超时;最终通过腾讯云 Web 终端粘贴脚本完成部署和后续修复。
验证状态:首次发现。
操作规则:
- 优先尝试 SSH key 接管,失败后不要无限诊断。
- 降级为“用户粘贴一次脚本,Agent 根据输出继续修复”。
- 脚本必须幂等,能重复执行关键步骤,不依赖交互输入。
- 出错后只要求用户贴关键日志,不让用户猜。
边界:涉及删除、停服务、覆盖配置时必须先确认影响面;脚本不要默认清空未知目录。
下次同类任务行动清单
- 本地原型完成后,先把
UPLOAD_FOLDER、UPLOAD_MAX_MB做成环境变量。 - 部署前检查服务器:
ss -ltnp | grep ':80'、systemctl status nginx、systemctl status caddy。 - Nginx 接管公网入口时,同时配置 Basic Auth、
client_max_body_size和超时。 - 上传 UI 只要支持大文件,就必须有进度条、超时提示和服务端保存等待提示。
- 上传接口优先 JSON 化;HTML 页面只负责渲染表单。
- 验收必须包含:小图片、超过 100MB 的视频、非法后缀、服务器目录落盘检查。
关联文档
- 复盘事实先行原则
- Claude完成报告核查心法
- 方法论笔记_LLM-plan卡点工作流_v1
- 同类部署(静态站 + 免备案上线):知识库网站免备案上线_香港轻量服务器方案