跳转到内容
getnextpdf.com

Pro 版本

Webview — 深度参考

本页记录公开的 NextPDF\Pro\Webview 接口面、字节范围请求/响应模型,以及公开着陆页之外的确切失败模式。

此能力随 NextPDF Pronextpdf/pro)交付,并以 Pro 层级的许可信封激活。没有该授权的部署不会加载此能力的类。比较各版本并获取授权

不存在逐功能的许可标志;代码随 Pro 版本交付。PSR-17 ResponseFactoryInterface / StreamFactoryInterface 与正文媒体类型是运行时构造函数参数,而非许可控制项。

Terminal window
composer require nextpdf/pro

NextPDF\Pro\Webview 下的公共类型:

  • LinearizedDocument——一份经校验、为投递准备好的线性化 PDF。
  • ByteRangeResponder——RFC 9110 字节范围 HTTP responder。
  • ByteRange——在一个表示上的一个可满足的包含式字节范围。
  • FirstPageProber——结构性的”整份下载完成前先给第一页”证明。

NextPDF\Pro\Webview\Exception 下的异常类型:

  • WebviewException(标记接口)、UnsupportedDocumentExceptionRangeNotSatisfiableException

一个带私有构造函数的 final readonly 类;通过命名构造函数实例化。

  • static fromBytes(string $bytes): self——通过 Core 读取侧的 LinearizationView::fromPdf() 解析字节。当文档未线性化、声明的 /L 与实际字节长度不匹配,或 /E 第一页结束偏移量不是文件内的一个正偏移量时,抛出 UnsupportedDocumentException
  • length(): int——以字节计的文档长度。
  • firstPagePrefixLength(): int——包含完整第一页的最小开头前缀:被钳制到文件长度的 /E 偏移量。
  • firstPageByteRange(): ByteRange——投递第一页的包含式范围 [0, /E - 1]。仅当前缀为空时抛出 RangeNotSatisfiableException(纵深防御;fromBytes() 已保证 0 < /E <= length)。
  • slice(int $firstByte, int $lastByte): string——带包含式偏移量的严格程序化切片;越界时抛出 RangeNotSatisfiableException
  • etag(): string——对字节的强、确定性 SHA-256 实体标签,在构造时记忆化一次。

公共只读属性:bytes(原始 PDF 字节)与 view(Core 的 LinearizationView)。

一个仅依据 PSR-7 / PSR-17 实现的 final readonly 类。

  • __construct(ResponseFactoryInterface $responses, StreamFactoryInterface $streams, string $contentType = 'application/pdf')——当 $contentType 含有控制字符时抛出 InvalidArgumentException(它会被插值进响应头与 multipart 部件头;CR/LF 及其他控制字节被拒绝以防止头注入)。
  • respond(LinearizedDocument $document, ServerRequestInterface $request): ResponseInterface——用文档自身的 ETag 应答一个针对线性化文档的范围请求。
  • respondToBytes(string $bytes, ServerRequestInterface $request, ?string $etag = null): ResponseInterface——应答一个针对任意字节的范围请求(一个应当支持范围、但无需线性化的表示)。当 etagnull 时,ETag 从字节派生。
  • firstPageResponse(LinearizedDocument $document): ResponseInterface——构建一个恰好携带第一页字节范围的 206;即”整份下载完成前先给第一页”的服务器推送形式。

一个用于单个可满足的包含式字节范围的 final readonly 值对象(RFC 9110 §14.1.2)。

  • __construct(int $firstByte, int $lastByte, int $contentLength)——强制可满足性 0 <= firstByte <= lastByte <= contentLength - 1;否则抛出 RangeNotSatisfiableException
  • length(): int——包含式跨度(lastByte - firstByte + 1),始终 >= 1
  • contentRange(): string——RFC 9110 §14.4 的 Content-Range 字段值 bytes first-last/length

公共只读属性:firstBytelastBytecontentLength

一个由 LinearizedDocument 构造的 final readonly 类。

  • prefixLength(): int——渲染第一页所需的最小开头字节数。
  • prefixFraction(): float——该前缀所占整份文件的比例(0.0–1.0);对一份零长度文件返回 1.0
  • hintStreamWithinPrefix(): bool——主提示流对象是否完整地位于第一页前缀之内(使该前缀本身就让一个阅读器能定位第 1 页对象)。提示流必须有正的长度。
  • isFirstPageSelfContained(): bool——组合后的结构性证明:一个正的前缀,它既容纳于文件之内,又完整地包含提示流。

ByteRangeResponder 遵循 RFC 9110 §14。在计算长度与一个强 SHA-256 ETag 之后,它读取 RangeIf-Range 头并作出决定:

条件状态说明
无适用的 Range,或 If-Range 与当前强 ETag 不匹配200 OK完整正文。仅 If-Range 的强实体标签形式被采纳(RFC 9110 §13.1.5)。
无法识别的范围单位或语法上无效的 Range200 OK该头被忽略(RFC 9110 §14.2)。
一个可满足的范围206 Partial Content携带 Content-Range
多个可满足的范围206 Partial Contentmultipart/byteranges,带一个派生的边界。
有效字节范围,无一可满足416 Range Not Satisfiable携带 Content-Range: bytes */length(RFC 9110 §15.3.7)。

每个响应都通告 Accept-Ranges: bytes 与强 ETag200206 响应还会设置 Content-TypeContent-Length

范围解析仅接受 bytes= 单位。它支持显式 first-last、开放式 first-(钳制到末尾),以及后缀 -N(最后的 N 字节;一个至少与表示一样大的后缀会选中其整体)。一个裸的 -(或任何其他格式错误的规格)会使整个 Range 头语法上无效,因此该头被忽略并返回完整的 200 OK 表示。一个 -0 后缀,或任何其首偏移量位于末尾处或之后的规格,是一个不可满足的规格并被丢弃;如果头中没有任何规格可满足,则响应为 416 Range Not Satisfiable。大的十进制偏移量在比较时不依赖整数溢出饱和,因此一个 30 位的 Range 值会被以与平台无关的方式处理。重叠的可满足范围会在构建任何正文之前被合并;真正不同(不重叠)的范围会被保留为各自独立的 multipart 部件。

WebviewException 是一个继承 Throwable 的标记接口;捕获它即可统一处理整个子系统。两个具体异常都实现它。

  • UnsupportedDocumentException(继承 InvalidArgumentException)——当字节不是一份可用的线性化文档时,由 LinearizedDocument::fromBytes() 引发。命名构造函数:notLinearized()(无 /Linearized 参数字典)、lengthMismatch($declaredLength, $actualLength)(声明的 /L 与实际长度不匹配——被截断、通过一次增量更新被追加到超出 /L,或不符合规范),以及 malformedFirstPageOffset($firstPageEndOffset, $length)/E 偏移量不是文件内的一个正偏移量)。
  • RangeNotSatisfiableException(继承 OutOfRangeException)——程序化切片错误,当一个包含式范围落在文档之外时,由 ByteRange::__construct()LinearizedDocument::slice() / firstPageByteRange() 引发。命名构造函数:outOfBounds($firstByte, $lastByte, $length)

HTTP responder 对客户端 Range抛出 RangeNotSatisfiableException——一个不可满足的 HTTP 范围是一个 416 响应(RFC 9110 §15.3.7),而非一个异常。那个异常保留给直接的程序化切片,在那里一个越界请求是调用方错误。当配置的 contentType 含有控制字符时,ByteRangeResponder::__construct() 抛出一个普通的 InvalidArgumentException(不是一个 WebviewException)。

responder 对每个请求所采纳的不同合并范围数量设有上限(multipart 范围放大这一类,Apache HTTPD CVE-2011-3192)。当一个请求要求的合并范围数多于该上限,或要求的总字节数多于整份表示时,Range 会被忽略,并返回一个完整的 200。multipart 边界被确定性地派生,并被反复重新派生直到它保证不会出现在正文内部,从而在排除边界碰撞的同时保持可复现的输出。

字节范围行为遵循 RFC 9110(HTTP Semantics):§14(范围请求)、§13.1.5(If-Range)、§14.4(Content-Range),以及 §15.3.7(416)。线性化文档布局是 ISO 32000-2 Annex F 的 Fast Web View 模型。本模块除其测试所验证的行为之外,不主张任何进一步的外部条款标识。

  • 当不需要第一页语义时,respondToBytes() 在任意字节上提供范围。
  • 一个 If-Range HTTP-date 验证器被视为不匹配 → 完整的 200(客户端只需重新抓取)。
  • ETag 是一个纯粹用作强缓存验证器的 SHA-256 哈希;本模块不执行任何签名或其他密码学操作,也不定义任何 FIPS 特定行为。

本页仅记录外部可观察的行为与受支持的公共 API 接口面。内部命名空间路径、辅助类、机制表、runbook 文件名与工单前缀不在范围内。