# 变更说明：8899 下载页增加 PDF 在线预览

> 原文档在 2026-09-03 下载中心误删事故中丢失，2026-09-19 依据会话记录重建（内容与原文一致）。

方式：零新依赖、浏览器原生 PDF 渲染、外科手术式改动。

## 改了什么

### 注释

文件顶部预览说明改为包含 `.pdf`，并注明 PDF 走 iframe + `?inline=1`，不受 2MB 预览上限限制。

### CSS（`PREVIEW_CSS`）

新增：

- `.pdf-frame`：`width:100%; border:none; border-radius:8px; background:#fff;`，默认高度 `calc(100vh - 230px)`（侧栏）
- `.content.pdf`：去掉内边距、取消 max-height 裁切，避免整页预览双滚动条
- `.content.pdf .pdf-frame`：整页高度 `calc(100vh - 240px)`
- `.preview-body .pdf-frame`：侧栏分栏高度 `calc(100vh - 230px)`

`LIST_CSS` 已拼接 `PREVIEW_CSS`，列表页右侧分栏自动带上这些样式。

### `previewKind(name)`

`.pdf`（大小写不敏感）返回 `'pdf'`。列表行因此带 `data-kind="pdf"`，文件名链接自动变为 `?preview=1`（沿用原有 `kind ? href + '?preview=1' : href`）。

### 新增 `?inline=1`（仅 PDF）

- 请求处理分支增加 `wantInline`。
- 仅当 `kind === 'pdf'` 时生效；其它文件忽略该参数，仍走原逻辑（预览或下载）。
- 新函数 `sendInlinePdf`：复用 `fs.createReadStream` 管道，响应头为
  - `Content-Type: application/pdf`
  - `Content-Disposition: inline`
  - `Content-Length`

### 预览渲染（不读 PDF 正文、不套 2MB 上限）

新增 `pdfIframe(filePath)`，产出：

```html
<iframe class="pdf-frame" src="<encodeURIComponent(文件名)>?inline=1" title="PDF 预览"></iframe>
```

- `sendPreview`：`kind === 'pdf'` 时走独立分支，调用 `previewChrome(..., 'pdf', iframe, false)`。
- `sendFragment`：`kind === 'pdf'` 时直接返回上述 iframe 片段，不 `readFileSync`。

### 其它

- 列表页底部提示文案补上「PDF」。
- Markdown / 文本预览、2MB 上限与「文件过大」提示、下载、排序、面包屑、返回上级、路径穿越守卫、XSS 转义均未改行为。

## PDF 预览实现方式

列表页点击 PDF 行 → 现有 JS `fetch(?fragment=1)` → 服务端返回 iframe → iframe 再请求同一文件 `?inline=1` → 浏览器内置 PDF 查看器渲染。

整页 `?preview=1` 同理，只是 iframe 包在 `previewChrome` 里。大 PDF 不进 Node 内存、不受 `PREVIEW_MAX` 限制。

## 验证结果（改造时实测）

| 检查项 | 结果 |
|---|---|
| 列表页 PDF 行 `data-kind="pdf"` 且链接 `?preview=1` | 通过（3 个 PDF） |
| `GET ...pdf?inline=1` | `Content-Type: application/pdf`，`Content-Disposition: inline`，正文以 `%PDF-1.7` 开头 |
| `GET ...pdf?preview=1` | HTML 含 `<iframe class="pdf-frame" src="...?inline=1">`，`tooLarge` 为 false |
| `GET ...pdf?fragment=1` | 仅返回 iframe 片段 |
| 14.7MB PDF（Belfer）fragment/preview | 仍为 iframe，**没有**「文件过大」提示 |
| 非 PDF 带 `?inline=1` | 忽略该参数，仍 `attachment` 下载 |
| `.md?fragment=1` | 仍返回 Markdown HTML |
| 无参 GET PDF | 仍 `application/octet-stream` + `attachment` |
