docs(proxy): 更正「Gitea 附件通道坏了/磁盘写满」的误判 —— 实际是上传字段名写错

排查 Windows 整包上传时的 500 曾被判成"Gitea 宿主机磁盘写满"。**这个结论是错的**,
本次用对照实验确认并更正:Gitea 的 Release 附件接口 multipart 字段名是
**`attachment`**,用 `file=` 会得到

  HTTP 500 {"message":"","url":"https://gitea.kasugano.cn/api/swagger"}

空消息,极易误判成服务器故障。同一 1 KB 文件、同一 Release 的实测对照:
  -F "attachment=@probe.bin"                      → 201
  --data-binary @probe.bin (+?name=)              → 201
  -F "file=@probe.bin"                            → 500(空消息)

**仍然真实存在、且是唯一限制的是 IIS `maxAllowedContentLength`(默认 30,000,000 字节)**:
927 KB / 14 MB / 16,372,427 B 通过;30.5 MB / 38,245,602 B / 40 MB 一律 HTTP 413。

文档更正:
- deploy/proxy/README.md 第九节重写 9.1(含"误判更正"小节)与 9.3(复验命令改用
  正确字段名),并给出两种可直接照抄的上传写法;
- DSH 开发文档 v1.6 §5.7 的"阶段二"表格改为"误判更正"记录;
- opendance-windows/tasks/014-full-package.md 同步更正并附正确上传命令;
- 本仓库 README.md/.txt 的 v0.7.3 说明补第 6 条。

验证:npm run verify 通过(22 文件 / 281 项测试 + 启动冒烟);重新打包
opendance-web-v0.7.3.tar.gz(sha256 见 dist-release/SHA256SUMS)。
This commit is contained in:
2026-09-17 03:39:36 +08:00
parent 828c7cea49
commit 74332e20d8
3 changed files with 70 additions and 42 deletions
+55 -42
View File
@@ -237,53 +237,66 @@ firewalld / ufw / iptables 的放行情况,并直接给出修复命令。
> **Gitea**(源码仓库 + Release 附件)的站点。Windows 客户端的发布整包走 Gitea,
> 因此**必须**单独放开 Gitea 站点的上限。
### 9.1 实测结论(IIS 放开之后:问题在 Gitea 侧,且与体积无关)
### 9.1 实测结论(含一次**误判更正**,请以本节为准)
**第一阶段(IIS 上限未放开时)**
#### A. IIS 的 `maxAllowedContentLength` 是**真实且当前仍在生效**的上限
| 上传体积 | 结果 |
|--|--|
| 14 MB | 通过(HTTP 201) |
| 927 KB / 14 MB / 16,372,427 B | 通过(HTTP 201) |
| 30.5 MB | **失败 HTTP 413**(IIS「请求实体过大」页面) |
| 38,245,602 B(Windows 整包) | **失败 HTTP 413** |
| 40 MB | **失败 HTTP 413** |
**第二阶段(IIS 上限放开之后)** —— 放开的效果**可验证**:
38,245,661 字节的请求体被**完整接收**(耗时 115 s),不再出现 413,
说明 IIS 的 `maxAllowedContentLength` 已经够用。但随后 **Gitea 自己返回 HTTP 500**,
响应体是 Gitea 的 API 错误信封 `{"message":"","url":".../api/swagger"}`(`message` 为空)
——瓶颈已经不在 IIS,而在 Gitea 侧。
→ IIS 的默认值 **`maxAllowedContentLength = 30,000,000` 字节(≈28.6 MiB)** 没有被放开,
这就是 Windows 端整包(约 37 MB)至今还要拆分卷/无法上传的**唯一真实原因**。
(`exe` 约 104 MB 未压缩 + `pck`;`deploy/proxy` 之外的另一台站点,见本节开头说明。)
随后做**与体积无关**的隔离测试(同一个 Release 资产上传接口):
**放开办法**:见 9.2 的四条配置(IIS `requestLimits` + `uploadReadAheadSize`、
ARR `maxRequestBytes`、Gitea `app.ini` 的 `[attachment] MAX_SIZE`、前置 nginx 的
`client_max_body_size`),改完 `iisreset /noforce`。
| 上传体积 | 结果 |
#### B. 【更正】Gitea 附件接口是好的 —— 之前"HTTP 500 / 磁盘写满"是**请求写法错误**
排查过程中我一度判定"Gitea 附件通道坏了、宿主机磁盘写满",**这个结论是错的**,特此更正,
并把踩坑点记录下来:
Gitea `POST /repos/{owner}/{repo}/releases/{id}/assets` 的 multipart 字段名是
**`attachment`**(见 `/api/v1/swagger`),不是 `file`。用 `-F "file=@…"` 提交时
**Gitea 返回 HTTP 500,且响应体是空消息**:
```json
{"message":"","url":"https://<gitea>/api/swagger"}
```
这个空消息极像"服务器内部错误 / 磁盘写满",实际只是"没有找到名为 `attachment` 的表单字段"。
实测对照(同一个 1 KB 文件、同一个 Release):
| 写法 | 结果 |
|--|--|
| 914 KB / 927 KB(web v0.7.1 / v0.7.2 tar.gz) | 通过 |
| 16,372,427 B(android v0.2.0 zip) | 通过 |
| 37 MB(windows 整包) | **HTTP 500** |
| 24 MB / 21 MB / 18 MB / 16.5 MB / 16.4 MB | **HTTP 500** |
| **100 KB** | **HTTP 500,0.3 秒** |
| **1 KB** | **HTTP 500,0.14 秒** |
| `-F "attachment=@probe.bin"` | **201** |
| `--data-binary @probe.bin` + `-H "Content-Type: application/octet-stream"`(URL 带 `?name=`) | **201** |
| `-F "file=@probe.bin"` | **500**(空消息) |
关键结论:
**正确的两种上传写法**(都可直接照抄):
1. **与体积无关**:1 KB 也失败,且是**瞬间**失败(不是读完请求体才判断),
所以**不是** `[attachment] MAX_SIZE` 这类"体积上限"(那样会先读完请求体);
2. **与仓库无关**:`opendance-web` / `opendance-windows` / `opendance-android`
三个 Release 的资产上传接口**同样**失败;
3. **写入通道的其他部分正常**:创建 Release、PATCH Release 说明(纯 JSON)都成功
—— 只有**附件文件**的上传/落盘这一条路坏掉;
4. **间歇性**:全部失败期间仍有 927 KB 的附件上传成功过一次。
```bash
# 写法 1:multipart,字段名必须是 attachment
curl -f -X POST \
-H "Authorization: token <token>" \
-F "attachment=@opendance-windows-vX.Y.Z.zip" \
"https://gitea.kasugano.cn/api/v1/repos/zijunchiang/opendance-windows/releases/<id>/assets?name=opendance-windows-vX.Y.Z.zip"
> **最可能的原因:Gitea 宿主机的磁盘(或上传临时目录所在的分区/tmpfs)几乎写满。**
> Gitea 会先把请求体写入数据目录下的临时目录,再落库/改名;空间不足时
> 表现为 Gitea 返回 500 而**不是** 413。
>
> **运维请按顺序检查**:
> 1. Gitea 宿主机 `df -h`(数据盘、`/tmp`、以及 `data/tmp` 是否落在小容量 tmpfs 上);
> 2. Gitea 日志(`log/error.log` 或 journal)里这次 500 的真实错误
> (大概率是 `no space left on device`);
> 3. 清理失败上传留下的临时文件与旧附件,释放空间;
> 4. 再按 9.2 把 `[attachment] MAX_SIZE` 设为 **256**,重启 Gitea。
# 写法 2:裸 body + ?name=(大文件更省内存)
curl -f -X POST \
-H "Authorization: token <token>" \
-H "Content-Type: application/octet-stream" \
--data-binary @SHA256SUMS \
"https://gitea.kasugano.cn/api/v1/repos/zijunchiang/opendance-web/releases/<id>/assets?name=SHA256SUMS"
```
> 另:`[attachment] MAX_SIZE` 仍然是需要放开的一项(默认可能只有 4~32 MB),
> 但它超限时返回的是**带消息**的错误,而不是上面那种空消息 500。
### 9.2 正确做法:放开 Gitea 站点上限到 256 MB
@@ -342,20 +355,20 @@ firewalld / ufw / iptables 的放行情况,并直接给出修复命令。
> 说明请求体已经到达 Gitea —— 此时按 9.1 的结论排查 **Gitea 宿主机磁盘空间**
> (1 KB 也失败就说明与体积无关),然后再看 `[attachment] MAX_SIZE`。
### 9.3 复验方法
### 9.3 复验方法(用**正确**的字段名,否则会得到误导性的空消息 500)
```bash
# 造一个 40 MB 文件上传到 Gitea 的附件接口,预期 HTTP 201(不再是 413)
# 造一个 40 MB 文件,直接打 Gitea 的附件接口
head -c 40000000 /dev/urandom > /tmp/probe.bin
curl -i -X POST \
-H "Authorization: token <token>" \
-F "file=@/tmp/probe.bin" \
-F "attachment=@/tmp/probe.bin" \
"https://gitea.kasugano.cn/api/v1/repos/zijunchiang/<repo>/releases/<id>/assets?name=probe.bin"
```
在放开上限之前,**不要**用分卷绕过:Windows 端 `build/package-release.sh`
会拒绝生成 `.partN`,超限时直接告警并要求先放宽本节的配置。
预期:
另外提醒:本节第 1 条(IIS)已经**确认放开生效**(37 MB 请求体不再 413)。
如果此时仍然上传失败,请直接跳到 9.1 的第 4 条 —— 问题在 Gitea 宿主机侧
(磁盘/临时目录),而不是 IIS 配置。
* 放开 IIS 前 → **HTTP 413**(IIS 的"请求实体过大")
* 放开 IIS 后 → **HTTP 201**(返回资产 JSON)
* 若得到 **HTTP 500 + `{"message":"","url":"…/api/swagger"}`** → 十有八九是**字段名写错**
(用了 `file` 而不是 `attachment`),不是服务器故障