Pro 版本
Webview
Webview 會在 HTTP 上提供一份已線性化(Fast Web View)的 PDF,讓用戶端能從一段短小的前導前綴(leading prefix)開始算繪第 1 頁,而檔案其餘部分仍在傳輸途中。它會把原始位元組包裹為一個 LinearizedDocument,透過一個 PSR-7 ByteRangeResponder 以 RFC 9110 partial-content 回應來回答 Range 請求,並能(透過 FirstPageProber)證明第一頁在該前綴中是自足的。
供應與授權
標題為「供應與授權」的區段此能力隨附於 NextPDF Pro(nextpdf/pro),並以一份 Pro 級授權封裝啟用。一個沒有該權利的部署不會載入此能力的類別。比較版本並取得授權。
沒有獨立的個別功能授權旗標。responder 會在執行階段接上你自己的 PSR-17 工廠——一個 ResponseFactoryInterface 與一個 StreamFactoryInterface——而媒體型別預設為 application/pdf,是一個建構子引數,而非授權開關。
composer require nextpdf/pro程式碼位於 NextPDF\Pro\Webview 命名空間下。
概念總覽
標題為「概念總覽」的區段一份線性化的 PDF 是這樣排版的:文件的第一頁——linearization 參數字典、主要的 hint stream,以及第 1 頁的物件——位於一個結束於 /E 偏移的前導區段。Webview 把那種排版轉成漸進式交付。
LinearizedDocument::fromBytes() 會透過 Core 的讀取端 LinearizationView 解析這些位元組(Pro 絕不重新實作 linearization 解析),並拒絕任何不是可用線性化文件的東西:根本未線性化、宣告的 /L 長度與真實位元組長度不符,或者一個 /E 第一頁結尾偏移不是檔案內的正向偏移。因此建構是全函式(total)的——一旦你持有一個 LinearizedDocument,它所暴露的每個偏移都是可信的。
ByteRangeResponder 接著回答一個 HTTP 請求。它僅針對 PSR-7 / PSR-17 實作,無框架耦合。它永遠通告 Accept-Ranges: bytes 與一個強而確定性的 SHA-256 ETag,依 RFC 9110 §14 解析用戶端的 Range 標頭,並回傳一個完整的 200 OK、一個 206 Partial Content 單一範圍、一個用於多個範圍的 206 multipart/byteranges 回應,或一個 416 Range Not Satisfiable。
FirstPageProber 是結構性證明這一側:它量化第一頁前綴、該前綴占整份檔案的比例,以及主要的 hint stream 是否完全落在其內——這正是讓讀取端能僅從前綴定位第 1 頁物件的屬性。
為何如此設計
標題為「為何如此設計」的區段Webview 絕不自行重新解析 linearization。它借用 Core 的讀取端 LinearizationView,因此交付層繼承了單一份經稽核的解析器,而非第二份會逐漸漂移的副本。建構是刻意全函式的。LinearizedDocument::fromBytes() 會在一開始就拒絕一個格式不良的排版,因此每個 Range 回應所信任的偏移都是先經驗證的。responder 只講 PSR-7 與 PSR-17,因此同一份程式碼能從任何 HTTP 堆疊提供一份線性化 PDF。正是那份紀律,讓漸進式範圍交付得以安全地大規模暴露給不可信的用戶端。
設計背景:大量文件產生。
漸進式 byte-range 服務如何運作
標題為「漸進式 byte-range 服務如何運作」的區段- 從算繪後的 PDF 位元組建構一個
LinearizedDocument。無效的輸入會在一開始就引發UnsupportedDocumentException。 - 把文件與傳入的 PSR-7
ServerRequestInterface交給ByteRangeResponder::respond()。responder 會讀取Range(以及選用的If-Range前置條件),並產生正確的 PSR-7ResponseInterface。 - 用戶端先請求前導前綴(或你以
firstPageResponse()推送它),算繪第 1 頁,再隨使用者捲動而請求其餘範圍。
byte-range 模型依 RFC 9110 §14.1.2 使用**包含式(inclusive)**偏移:一個 ByteRange 是某個 contentLength 表示上的 firstByte–lastByte,而其 Content-Range 欄位為 bytes first-last/length。
行為合約
標題為「行為合約」的區段LinearizedDocument::fromBytes()是全函式的:一份非線性化文件、一個/L不符,或一個非正值/越界的/E偏移,都各自引發UnsupportedDocumentException,而非產生一份不安全的文件。ETag是涵蓋確切位元組的一個強 SHA-256 entity-tag,在建構時記憶化一次。相同的算繪輸入會產生相同的位元組,因而產生相同的ETag,使快取與If-Range的行為可預期。- 一個沒有任何適用
Range的請求會回傳帶有完整主體的200 OK。一個與當前強ETag不符的If-Range會導致Range被忽略並回傳一個完整的200(RFC 9110 §13.1.5)。只有強 entity-tag 形式的If-Range會被遵守;一個 HTTP-date 的If-Range會被視為不符。 - 一個無法辨識的範圍單位或一個語法上無效的
Range會被忽略,並回傳一個完整的200(RFC 9110 §14.2)。 - 一個可滿足的範圍會回傳帶有
Content-Range的206 Partial Content;多個可滿足的範圍會回傳206multipart/byteranges。有效但無一可滿足的 byte range 會回傳416,並帶有Content-Range: bytes */length(RFC 9110 §15.3.7)。 firstPageResponse()會發出一個恰好攜帶第一頁 byte range[0, /E - 1]的206——亦即「在完整下載之前的第一頁」的伺服器推送形式。
程式碼範例——快速上手
標題為「程式碼範例——快速上手」的區段以下反映已記錄的公開 API。本儲存庫並未為此模組隨附一個可執行的範例。
use NextPDF\Pro\Webview\LinearizedDocument;use NextPDF\Pro\Webview\ByteRangeResponder;
$document = LinearizedDocument::fromBytes($pdfBytes);$responder = new ByteRangeResponder($responseFactory, $streamFactory);
$response = $responder->respond($document, $request);程式碼範例——第一頁推送與探測
標題為「程式碼範例——第一頁推送與探測」的區段use NextPDF\Pro\Webview\LinearizedDocument;use NextPDF\Pro\Webview\ByteRangeResponder;use NextPDF\Pro\Webview\FirstPageProber;use NextPDF\Pro\Webview\Exception\UnsupportedDocumentException;
try { $document = LinearizedDocument::fromBytes($pdfBytes);} catch (UnsupportedDocumentException $e) { // Not a usable linearized document — fall back to plain full delivery. // ... return;}
$prober = new FirstPageProber($document);if ($prober->isFirstPageSelfContained()) { // Push exactly the first page's bytes for an instant render. $response = (new ByteRangeResponder($responseFactory, $streamFactory)) ->firstPageResponse($document);}邊界案例與陷阱
標題為「邊界案例與陷阱」的區段- Webview 需要一份真正線性化的 PDF。如果算繪後的文件未線性化,請在算繪時啟用 linearization,或以一般的完整交付提供它——當你只需要範圍支援、而非第一頁語意時,
respondToBytes()仍能在任意(非線性化)位元組上提供範圍。 - 增量更新很重要:一份被附加到超出其宣告
/L之外的文件會以長度不符被拒絕,因為 byte-range 偏移將不再可信。 - responder 會為每個請求所遵守的相異範圍數目設上限。一個請求若要求比上限更多的合併(coalesced)範圍,或比整份表示更多的總位元組,其
Range會被忽略,並改服務一個完整的200。
第一頁前綴是 /E 第一頁結尾偏移夾限(clamp)至檔案長度,因此 FirstPageProber::prefixFraction() 會回報初始抓取相對於整份檔案有多小——對一份多頁文件而言,這正是 Fast Web View 的全部重點。回應建構會切取記憶體內的位元組字串;成本與所選取的位元組成正比。ETag 每份文件計算一次。請以具代表性的文件量測。
安全注意事項
標題為「安全注意事項」的區段請將輸入視為不可信。LinearizedDocument::fromBytes() 會在任何偏移被使用之前驗證 linearization 不變式。responder 會拒絕一個含控制字元的 contentType 以防止標頭注入,推導出一個保證不會出現在主體內的 multipart 邊界,並合併重疊的範圍且為其數目與總大小設界,以抵禦 multipart range-amplification 這一類阻斷服務攻擊(Apache HTTPD CVE-2011-3192)。本模組不會記錄任何文件內容。
一致性
標題為「一致性」的區段byte-range 交付遵循 RFC 9110(HTTP Semantics)——§14 用於範圍請求、§13.1.5 用於 If-Range、§15.3.7 用於 416。線性化文件模型是 ISO 32000-2 Annex F 所描述的 Fast Web View 排版。本模組除了其測試所驗證的行為之外,不主張任何進一步的外部條款識別碼。
Enterprise 邊界註記
標題為「Enterprise 邊界註記」的區段Enterprise 不會改變 Webview 的行為。Enterprise 加入了另行記錄的更高階一致性與封存功能;在 byte range 上提供一份線性化 PDF 並不需要它們。
發佈邊界
標題為「發佈邊界」的區段本頁僅記載外部可觀察的行為與所支援的公開 API 表面。內部命名空間路徑、輔助類別、機制表格、runbook 檔名,以及工單前綴皆不在範圍內。