AGENTS.md
30d8238
与文件大小无关,任意大小的 Excel 都会失败。
web/src/utils/request.js:18-24:
web/src/utils/request.js:18-24
if (store.getters.token) { config.headers['Authorization'] = 'Bearer ' + getToken() config.headers['Content-Type'] = 'application/json' // 无条件覆盖 }
只要用户已登录(上传场景必然成立),所有请求的 Content-Type 都被强制改为 application/json,包括本次的 multipart 上传。
Content-Type
application/json
于是请求体是 multipart 格式、请求头却声明为 JSON。服务端 server/app/goauto/sybinnercode/handler.go:54 的 c.FormFile("file") 无法解析 multipart 而返回错误,进入 :56 输出「没有收到 10MB 以内的 Excel」。
server/app/goauto/sybinnercode/handler.go:54
c.FormFile("file")
:56
web/src/api/goauto/syb-inner-codes.js:6:
web/src/api/goauto/syb-inner-codes.js:6
headers: { 'Content-Type': 'multipart/form-data' }
手写该值会丢掉 boundary。正确的头部形如 multipart/form-data; boundary=----WebKitFormBoundaryXXX,boundary 由浏览器在序列化 FormData 时生成。即使修复了主因,只要这行仍在,服务端依旧无法解析。
multipart/form-data; boundary=----WebKitFormBoundaryXXX
两处必须同时修,只修其一仍然失败。
handler.go:56 把「表单字段名不匹配」「Content-Type 不正确」「请求体超过上限」等不同失败统一输出为「没有收到 10MB 以内的 Excel」,导致排查方向被引向文件体积,而真实原因是请求头。
handler.go:56
服务端的大小上限本身是正确的:MaxUploadBytes = 10 * 1024 * 1024(types.go:11)、MaxBytesReader 留了 1MB 表单开销(handler.go:53)、parser.go:36-37 另有独立的超限提示。
MaxUploadBytes = 10 * 1024 * 1024
types.go:11
MaxBytesReader
handler.go:53
parser.go:36-37
request.js
web/src/utils/request.js:仅在请求体不是 FormData 时才设置 JSON 的 Content-Type;Authorization 的设置逻辑保持不变。
web/src/utils/request.js
FormData
Authorization
config.headers['Authorization'] = 'Bearer ' + getToken() if (!(config.data instanceof FormData)) { config.headers['Content-Type'] = 'application/json' }
web/src/api/goauto/syb-inner-codes.js:6:删除手写的 headers: { 'Content-Type': 'multipart/form-data' },交由浏览器自动生成带 boundary 的头部。
全局检查:确认项目中其他以 FormData 提交的接口没有同类手写 Content-Type 的写法;若有一并修正,并在工单列出清单。
server/app/goauto/sybinnercode/handler.go:54-57
parser.go:37
ValidateUpload
.xlsx
multipart/form-data; boundary=...
go test ./app/goauto/sybinnercode/...
Troubleshooting
待验收(实现提交 9d23b44,已推送至 origin/main)。
9d23b44
origin/main
已核对 #139 最新正文、当前 main@6f611a0 与工作区;仅有无关未跟踪项 gitea.zip、prototypes/130/,将保持不动。
main@6f611a0
gitea.zip
prototypes/130/
全局排查确认共有三条真实 FormData 路径:档口入库码导入、用户头像上传、通用 ImageCropper。后两条未手写 multipart 头,但同样会被当前全局拦截器覆盖,因此在拦截器统一修复后一起恢复;仅档口入库码 API 还需删除手写 multipart/form-data。本单无数据库、权限、状态机和长期文档影响,按已确认方案进入实施。
multipart/form-data
已按工单范围完成并推送:9d23b44 fix(upload): preserve multipart boundaries (#139)。
9d23b44 fix(upload): preserve multipart boundaries (#139)
boundary
web/src/api/goauto/syb-inner-codes.js
web/src/views/profile/userAvatar.vue
web/src/components/ImageCropper/index.vue
pnpm test:unit
pnpm build:prod
python dev_scripts/harness.py check --strict
.\scripts\verify.ps1 -Component all
git diff --check
首次定向 Jest 执行曾因 Windows 临时缓存文件缺失失败;清理 Jest cache 后重跑通过,全量单测随后也通过。该失败与产品代码无关。
工单保持打开,等待人工验收。
用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。
No dependencies set.
The note is not visible to the blocked user.
所属与来源
AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。根因(提交
30d8238复核)与文件大小无关,任意大小的 Excel 都会失败。
主因:全局请求拦截器无条件覆盖 Content-Type
web/src/utils/request.js:18-24:只要用户已登录(上传场景必然成立),所有请求的
Content-Type都被强制改为application/json,包括本次的 multipart 上传。于是请求体是 multipart 格式、请求头却声明为 JSON。服务端
server/app/goauto/sybinnercode/handler.go:54的c.FormFile("file")无法解析 multipart 而返回错误,进入:56输出「没有收到 10MB 以内的 Excel」。次因:API 层手写 Content-Type 丢失 boundary
web/src/api/goauto/syb-inner-codes.js:6:手写该值会丢掉 boundary。正确的头部形如
multipart/form-data; boundary=----WebKitFormBoundaryXXX,boundary 由浏览器在序列化 FormData 时生成。即使修复了主因,只要这行仍在,服务端依旧无法解析。两处必须同时修,只修其一仍然失败。
附带问题:错误提示掩盖真实原因
handler.go:56把「表单字段名不匹配」「Content-Type 不正确」「请求体超过上限」等不同失败统一输出为「没有收到 10MB 以内的 Excel」,导致排查方向被引向文件体积,而真实原因是请求头。服务端的大小上限本身是正确的:
MaxUploadBytes = 10 * 1024 * 1024(types.go:11)、MaxBytesReader留了 1MB 表单开销(handler.go:53)、parser.go:36-37另有独立的超限提示。目标
非目标
request.js的其他拦截逻辑(鉴权、响应处理、错误提示保持原样)。实施方案
前端
web/src/utils/request.js:仅在请求体不是FormData时才设置 JSON 的Content-Type;Authorization的设置逻辑保持不变。web/src/api/goauto/syb-inner-codes.js:6:删除手写的headers: { 'Content-Type': 'multipart/form-data' },交由浏览器自动生成带 boundary 的头部。全局检查:确认项目中其他以
FormData提交的接口没有同类手写 Content-Type 的写法;若有一并修正,并在工单列出清单。服务端
server/app/goauto/sybinnercode/handler.go:54-57:区分失败原因——MaxBytesReader上限时提示「Excel 超过 10MB 上限」,与parser.go:37的现有文案保持一致;ValidateUpload的文件名、大小与文件头校验保持原样。安全边界
Authorization头在所有请求上照常携带。验收标准
.xlsx文件。Content-Type为浏览器生成的multipart/form-data; boundary=...,未被覆盖为application/json。application/json发送,既有功能无回归。验证方式
go test ./app/goauto/sybinnercode/...依赖、并行与风险
request.js是全局拦截器,改动影响所有请求。缓解:改动仅为增加一个FormData判断条件,不触碰其他分支;验收含普通接口的回归检查。文档影响
Troubleshooting记录该类上传失败的定位方法,再按 Wiki-first 门禁更新。状态
待验收(实现提交
9d23b44,已推送至origin/main)。开始实施
已核对 #139 最新正文、当前
main@6f611a0与工作区;仅有无关未跟踪项gitea.zip、prototypes/130/,将保持不动。全局排查确认共有三条真实 FormData 路径:档口入库码导入、用户头像上传、通用 ImageCropper。后两条未手写 multipart 头,但同样会被当前全局拦截器覆盖,因此在拦截器统一修复后一起恢复;仅档口入库码 API 还需删除手写
multipart/form-data。本单无数据库、权限、状态机和长期文档影响,按已确认方案进入实施。实施完成,待验收
已按工单范围完成并推送:
9d23b44 fix(upload): preserve multipart boundaries (#139)。实现结果
FormData请求设置application/json;鉴权头逻辑保持不变。multipart/form-data,由浏览器生成带boundary的请求头。web/src/api/goauto/syb-inner-codes.jsweb/src/views/profile/userAvatar.vueweb/src/components/ImageCropper/index.vue仅第一条存在手写 multipart 头;后两条未手写,随全局拦截器修复一并恢复。
自动化验证
go test ./app/goauto/sybinnercode/...:通过。pnpm test:unit:8 个套件、31 个测试全部通过。pnpm build:prod:通过。python dev_scripts/harness.py check --strict:通过。.\scripts\verify.ps1 -Component all:通过(Server 全量测试/构建、Web lint/构建、Android 单测与 debug APK 构建)。Web lint、CSS 与包体积仅输出既有警告,无错误。git diff --check:通过。首次定向 Jest 执行曾因 Windows 临时缓存文件缺失失败;清理 Jest cache 后重跑通过,全量单测随后也通过。该失败与产品代码无关。
未验证与文档影响
工单保持打开,等待人工验收。
用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。