上传前必须调用服务端权限预检接口,用户点击上传时前端发起get请求校验权限,后端返回实时权限状态(如maxsize、allowedtypes),前端据此动态更新按钮状态、input.accept属性及客户端校验逻辑,并配合websocket实现权限变更的秒级响应,所有权限决策和服务端二次校验均不可省略。

上传前必须调用服务端权限预检接口
浏览器里选文件时,input[type="file"] 的 change 事件根本拿不到真实路径,也查不了用户当前有没有上传权。所谓“动态开放”,不是靠 JS 拦住 input 或隐藏按钮,而是每次点击上传动作前,必须发起一次轻量 HTTP 请求,让服务端实时判断。
典型流程是:用户点「上传」→ 前端发 GET /api/upload/permit?path=/reports → 后端查角色、目录写权限、配额余量 → 返回类似:
{"ok": true, "maxSize": 20971520} 或 {"ok": false, "reason": "role_denied"}
- 不要只看响应 JSON 的
ok字段;必须处理401(未登录)、403(权限拒绝)、网络超时等底层错误 - 禁止把预检结果缓存在
localStorage里——权限可能被管理员秒改,缓存会立刻失效 - 预检通过后,仍要确保后续上传请求携带有效认证凭证(如
Authorization: Bearer <token></token>)
按钮状态要根据预检结果实时切换
不能等用户点了才去校验,更不能“灰掉按钮但不说明原因”。真实体验中,按钮是否可点、是否显示 tooltip、是否禁用 input[type="file"],都得由预检响应驱动。
例如:
— 收到 {"ok": false, "reason": "quota_exceeded"} → 按钮置灰 + 显示提示 “本月配额已用完(剩余 0 MB)”
— 收到 {"ok": false, "reason": "path_readonly"} → 隐藏整个上传区域,或仅保留「申请权限」按钮
— 收到 {"ok": true, "allowedTypes": [".pdf", ".xlsx"]} → 同步更新 input.accept 属性,并在 JS 校验中复用该白名单
- 避免用 CSS
display: none完全隐藏控件——屏幕阅读器和键盘导航用户会丢失上下文 - 如果预检返回了
maxSize,前端files[0].size校验必须严格比对,不能只依赖input.accept - 用户切换目录或文件夹时,必须重新触发预检,不能复用上一个路径的结果
WebSocket 用于权限变更后的实时同步
多人协同场景下,“动态开放”不是一次性动作。比如管理员刚把某用户从 viewer 升为 editor,前端需要秒级响应,而不是等用户刷新页面或重点上传。
实现方式是:建立带身份与上下文的 WebSocket 连接,例如:const ws = new WebSocket("wss://api.example.com/ws?fileId=doc-123&userId=u789");
- 服务端收到权限变更(如 REST 调用
PATCH /files/doc-123/permissions)后,只向关联fileId的在线连接广播最小化消息,例如:{"type":"permission_update","target":"file:doc-123","role":"editor"} - 前端监听到该消息,且当前打开的是
doc-123→ 立即启用上传按钮、恢复input可用状态、更新 accept 白名单 - 绝对不要在前端做“用户角色 === 'admin' 就放开上传”的判断——所有权限决策必须来自服务端下发的状态
后端二次校验不可省略
即使前端一切顺利,真实上传请求到达后,服务端仍要完整走一遍权限链:解析 JWT、查用户当前角色、验证目标路径写权限、检查配额、核对文件扩展名(不信任 filename 字段)、用魔术字节确认真实类型。
- Express + multer 示例中,
limits.fileSize是基础防护,但不能替代业务逻辑校验 - 上传中间件收到
multipart/form-data后,必须重新调用权限服务,不能复用预检时的缓存结果 - 如果预检返回
maxSize: 10MB,而上传请求里实际文件是 12MB,后端应直接拒收并返回明确错误,而非让 multer 解析失败再抛错
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











