上传 PDF

当前内容契约更新:固定来源 PDF 已恢复为 paper_access 开关下的鉴权字节, 关闭404,qa_、别名、Range、历史原件不能绕过。新内容在独立 qatlas-content 桶的 content/ 命名空间不可变发布:source 绑定确切 PDF SHA,arXiv vN 是语义版本、 S3VersionId 是物理版本、parse revision 是独立不可变身份;三者不可混用。 同 paper/SHA 可复用 source;冻结导入别名不同字节409(overwrite也不替换)。 解析上传必须完整支持的 Middle+Markdown,保留所有成员原名/字节,MD-only拒收; manifest最后核验发布且PG必需,重传新revision,不覆盖历史。旧MD/JSON/images忽略, 只在内容/PDF访问按需冻结旧PDF,不bulk回填、不删除旧桶;没有nightly/boot/入库自动推理。 下文涉及旧三桶覆盖、仅full.md+images、原PDF410/外部直链的段落保留为历史背景, 不作为当前操作规则。当前API/上传/续读统一见论文内容契约 与Upload API;完整read视图不是原Middle JSON。

当你手里已经有 PDF——本地论文、扫描件、preprint 私下流传版本——用 qatlas contrib pdf 直接推到 server。(用自己的 MinerU 配额把 PDF 解析成 markdown 再推回,走 qatlas contrib mineru。)

前置条件

  • PAT 带 papers:write scope

  • arXiv ID 必须带版本后缀(v1 / v2),因为 server 按 <id>v<n> 寻址对象

上传 PDF

最小用例:

qatlas contrib pdf 2501.00010v1 --pdf paper.pdf

论文元数据(题目 / 作者 / 摘要 / 引用)由服务器从 OpenAlex 上游同步进 PostgreSQL paper registry(v0.7.0 起),不再走 upload 端点。

推送 MinerU 解析结果

把 PDF 解析成 markdown + 图片再推回 server,走 qatlas contrib mineru——它用你自己的 MinerU 配额跑解析,再把完整 bundle(full.md + 每张 images/*)一次推给 server 的 upload-mineru 端点。server 解包、把 full.md 写到 markdown bucket、每张 images/<name> 写到 images bucket,保证一篇论文的 md 和图片同时落到对应桶里。

sha256 去重语义

server 端的 object 都带 x-amz-meta-sha256 metadata,所以同一字节再次上传是 200 OK + {unchanged:true} 短路——零写入、幂等。换句话说:

场景

结果

第一次上传

201 Created,对象写入

重传完全相同字节(哪怕换机器、换路径)

200 OK {unchanged:true, ...} —— 安全可重试

上传不同字节到同一 arxiv_id

409 Conflict,body 含 existing_sha256 + new_sha256 + existing_path,旧对象不动

上传不同字节 + --overwrite

201 Created,新版本写入;旧版本被 bucket versioning 自动保留

contrib mineru 推送时整 bundle 一起 sha 校验 + 每个解出来的对象(md / 每张图)再各自 sha dedup。整 bundle 完全没变直接全部 unchanged 短路;部分变则只重写真的不同的对象。

client 端 sha256 校验

client 在上传前 stream sha256,作为 ?expected_sha256=<hex> query 传给 server。server stage 完字节后比对,不匹配 → 400 + {actual_sha256, expected_sha256},任何 S3 写之前就拒。

--overwrite 什么时候用

只在你确定要替换旧对象时用。理由:

  1. dedup 已经挡了"同字节重传"的常见无用覆盖

  2. 真的字节变了 → 通常是"有意义的修订",值得显式确认

  3. 即使你 --overwrite 了,旧版本仍在 S3 noncurrent versions 里,可恢复

contrib mineru 走 --overwrite 时是bundle 级别——markdown + 每张图都会按各自 sha 决定写还是 unchanged,不会用整 zip 一刀切。

并发安全(多 client 同时上传)

server 用 S3 If-None-Match: "*" conditional PUT,多 client 同时上传同一 arxiv_id 的同一字节 → 全部短路 200 unchanged,保证最多一个 201。不同字节并发 → 一个 201 + 其余 409,不可能静默覆盖。

contrib mineru 内部每张图、每个 md 各自走这条 conditional PUT 流水线;保证 markdown 写之前所有 image 先落盘(markdown 是 completion marker)。

参看 对象寻址(即 storage-architecture)和 上传 API 详解 了解底层语义。

完整 flags

qatlas contrib pdf

Flag

必填

默认

含义

<arxiv_id>

✅

—

必含版本(v1 等)

--pdf <path>

✅

—

本地 PDF 文件

--overwrite

❌

false

允许覆盖

qatlas contrib mineru

本地跑 MinerU 解析 + 推回完整 bundle(markdown + 全部图片)。完整 flags 见 用 MinerU 解析 PDF。

加 通用 client flags 全部支持。

响应解读

成功响应(201 / 200):

{
  "arxiv_id": "2501.00010v1",
  "pdf_path": "pdf/2501/2501.00010v1.pdf",
  "pdf_sha256": "ab12cd34...",
  "pdf_bytes": 1234567,
  "pdf_unchanged": false,
  "unchanged": false,
  "overwritten": false
}

冲突响应(409):

{
  "detail": "PDF for 2501.00010v1 already exists with different content",
  "existing_sha256": "ab12...",
  "new_sha256": "ef56...",
  "existing_path": "pdf/2501/2501.00010v1.pdf",
  "hint": "Pass --overwrite to replace; old version retained in S3 versioning."
}

怎么列已上传的论文

# 还没解析过 MinerU 的列表
qatlas contrib mineru --no-push   # 不真跑,只展示候选

下一步