入別只演示預(yù)覽成功:真實(shí)環(huán)境下的預(yù)覽鏈路與排查指南)
“做PDF導(dǎo)入別只演示預(yù)覽成功”——這句話我看了很久越想越覺(jué)得像一句給你留了面子、實(shí)際上已經(jīng)懟到臉上的提醒。做文件導(dǎo)入功能十年我見(jiàn)過(guò)太多演示現(xiàn)場(chǎng)打開(kāi)一個(gè)幾百KB的精選PDF點(diǎn)一下上傳預(yù)覽窗口唰地彈出來(lái)滿場(chǎng)點(diǎn)頭。等產(chǎn)品上線之后用戶的文件一到手白屏、卡死、亂碼、找不到文件樣樣都能湊齊。預(yù)覽成功這件事在演示環(huán)境里是結(jié)果在真實(shí)環(huán)境里只是起點(diǎn)。這個(gè)提醒適合誰(shuí)所有在做PDF導(dǎo)入、文件預(yù)覽、辦公套件、低代碼平臺(tái)、企業(yè)應(yīng)用的人。前端、客戶端、產(chǎn)品經(jīng)理都算。它要解決的是一個(gè)很樸素的問(wèn)題你做的PDF導(dǎo)入到底能不能扛住用戶手里那堆五花八門(mén)的PDF如果只想把演示做完這篇文章你可以關(guān)掉了。如果想在交付之前少挨幾頓罵我建議你把下面的鏈路、方案和排查表看完認(rèn)真對(duì)照一遍自己的代碼。1. “預(yù)覽成功”是最危險(xiǎn)的演示結(jié)論1.1 一條預(yù)覽鏈路每一環(huán)都可能掉鏈子PDF從進(jìn)入你的系統(tǒng)到在屏幕上顯示出來(lái)中間走過(guò)的路遠(yuǎn)比大多數(shù)人想的長(zhǎng)。它要經(jīng)過(guò)文件拾取、類(lèi)型識(shí)別、數(shù)據(jù)讀取、內(nèi)容解析、頁(yè)面渲染、交互響應(yīng)這六個(gè)環(huán)節(jié)每一環(huán)都依賴不同的環(huán)境因素。你演示時(shí)用的那個(gè)PDF通常是精心挑選過(guò)的體積小、頁(yè)面少、字體標(biāo)準(zhǔn)、沒(méi)有加密、沒(méi)有表單、文件名還是英文的。用戶手里的PDF是什么樣的掃描件、加密件、五百頁(yè)的彩色施工圖、文件名帶空格和中文、甚至是從某個(gè)老舊的業(yè)務(wù)系統(tǒng)里導(dǎo)出的畸形文件。我在一次給客戶做系統(tǒng)驗(yàn)收時(shí)就栽過(guò)跟頭。演示用的PDF是32KB的純文字合同預(yù)覽秒開(kāi)客戶當(dāng)場(chǎng)點(diǎn)頭。結(jié)果上線第一天客戶上傳了一個(gè)他們實(shí)際的檔案八百多頁(yè)掃描件單頁(yè)是高清圖文件體積接近300MB。預(yù)覽層直接卡住不動(dòng)瀏覽器標(biāo)簽頁(yè)轉(zhuǎn)了幾分鐘最后彈出“無(wú)響應(yīng)”。那次之后我們花了兩周重構(gòu)預(yù)覽方案才把大文件的預(yù)覽從“能打開(kāi)”變成了“能流暢翻頁(yè)”。預(yù)覽成功只說(shuō)明你在一條精心準(zhǔn)備的路徑上走通了。而生產(chǎn)環(huán)境里的路徑分支比想象中多得多。1.2 用戶的吐槽比驗(yàn)收文檔真實(shí)一百倍如果不信去看各個(gè)社區(qū)里用戶對(duì)PDF預(yù)覽的吐槽幾乎每條都能對(duì)應(yīng)到一個(gè)技術(shù)缺陷。有人搜“adobe reader pdf縮略圖預(yù)覽失效補(bǔ)丁”說(shuō)明連系統(tǒng)文件管理器里的縮略圖這種最基礎(chǔ)的預(yù)覽能力都可以壞掉有人問(wèn)“win10怎么永久關(guān)閉預(yù)覽窗格”“win11永久關(guān)閉預(yù)覽窗格”說(shuō)明資源管理器的預(yù)覽窗格在某些機(jī)器上卡到用戶寧可關(guān)閉功能還有人反映“某銀行類(lèi)App在我鴻蒙手機(jī)上卡住了”以及“App里既打不開(kāi)預(yù)覽下載的文件系統(tǒng)里又找不到”——這兩條放在一起特別扎心用戶在App里下載了一個(gè)PDFApp內(nèi)打不開(kāi)跑到文件管理器里又找不到等于這個(gè)文件人間蒸發(fā)了。同樣是預(yù)覽我們?cè)趍ac上使用Sublime這類(lèi)編輯器時(shí)已經(jīng)習(xí)慣了“選中即預(yù)覽”的絲滑體驗(yàn)Markdown文檔光標(biāo)切過(guò)去就能看到渲染結(jié)果。用戶的心智已經(jīng)被這些產(chǎn)品拉高了他們默認(rèn)PDF導(dǎo)入也應(yīng)該如此流暢、穩(wěn)定。一旦你做的功能達(dá)不到這個(gè)預(yù)期用戶不會(huì)覺(jué)得“這個(gè)產(chǎn)品預(yù)覽能力有限”只會(huì)覺(jué)得“這個(gè)產(chǎn)品有問(wèn)題”。這些散落在網(wǎng)絡(luò)各處的吐槽本質(zhì)上是在替用戶喊一句話預(yù)覽不是“點(diǎn)開(kāi)能看到第一頁(yè)”就算數(shù)它得在各種設(shè)備、各種系統(tǒng)、各種異常文件面前都站得住。2. PDF導(dǎo)入到預(yù)覽每一步都可能斷2.1 四個(gè)最容易掉鏈子的環(huán)節(jié)第一個(gè)環(huán)節(jié)是文件來(lái)源。用戶的PDF不一定來(lái)自本地文件選擇器它可能來(lái)自聊天工具轉(zhuǎn)發(fā)、企業(yè)網(wǎng)盤(pán)下載、郵件附件甚至是從一個(gè)壓縮包里拖出來(lái)的。經(jīng)過(guò)聊天工具轉(zhuǎn)發(fā)的文件文件名會(huì)被改掉后綴可能被篡改從網(wǎng)盤(pán)同步下來(lái)的文件可能只有部分內(nèi)容被同步到本地從壓縮包直接拖拽出的PDF有時(shí)候系統(tǒng)給它的MIME類(lèi)型就是空的或者干脆是application/octet-stream。第二個(gè)環(huán)節(jié)是類(lèi)型識(shí)別。很多實(shí)現(xiàn)會(huì)直接用input框的accept屬性加上擴(kuò)展名判斷或者只讀取file.type。問(wèn)題在于文件管理器上報(bào)的MIME類(lèi)型并不總是準(zhǔn)確尤其在中國(guó)市場(chǎng)很多文件管理器遇到未知類(lèi)型時(shí)一律給application/octet-stream。你在本地自測(cè)時(shí)用的是標(biāo)準(zhǔn)PDFfile.type正常返回application/pdf一切順利。但真實(shí)用戶手里的文件很可能被識(shí)別成“未知類(lèi)型”直接被你的過(guò)濾器攔掉了。第三個(gè)環(huán)節(jié)是數(shù)據(jù)讀取。拿到文件之后要把它讀進(jìn)內(nèi)存變成可渲染的數(shù)據(jù)這里常見(jiàn)的做法有FileReader讀成ArrayBuffer、讀成Base64、或者直接用Blob生成Object URL。開(kāi)場(chǎng)白里提的一句“預(yù)覽成功”其實(shí)就埋在這——你演示時(shí)用的是小文件讀進(jìn)內(nèi)存毫無(wú)壓力用戶那里可能是上百M(fèi)B的大文件Base64轉(zhuǎn)字符串會(huì)讓內(nèi)存直接翻幾倍前端崩潰只是個(gè)時(shí)間問(wèn)題。第四個(gè)環(huán)節(jié)是預(yù)覽渲染。這里涉及的技術(shù)選型更多iframe、embed、object標(biāo)簽、PDF.js、系統(tǒng)原生預(yù)覽組件。每一種都有各自的兼容性邊界。比如Chrome自帶PDF閱讀器你用iframe嵌入一個(gè)Blob URLChrome會(huì)直接接管渲染看著一切正常但到了某些WebView內(nèi)核里iframe加載PDF可能會(huì)觸發(fā)下載而不是預(yù)覽。這一環(huán)的坑屬于項(xiàng)目上線后才會(huì)集中爆發(fā)的類(lèi)型。2.2 “能打開(kāi)”不等于“能用”預(yù)覽要分層次驗(yàn)收我后來(lái)給自己定了一套“預(yù)覽質(zhì)量分層”的標(biāo)準(zhǔn)強(qiáng)烈建議每個(gè)做文件導(dǎo)入的人都套用一下用來(lái)判斷自己的功能到底處在哪個(gè)水平。層次表現(xiàn)演示時(shí)最容易卡在哪第一層能顯示第一頁(yè)加密PDF、損壞PDF在這一步就會(huì)失敗第二層能翻頁(yè)大文件渲染內(nèi)存不足或渲染太慢第三層能縮放、能旋轉(zhuǎn)高分辨率掃描件縮放卡頓第四層能搜索文本純圖片掃描件沒(méi)有文字層搜索無(wú)結(jié)果第五層能填寫(xiě)表單、能保持格式復(fù)雜表單和字體缺失會(huì)讓版式錯(cuò)亂第六層能按原樣打印CSS樣式干擾、分頁(yè)錯(cuò)位問(wèn)題一堆大多數(shù)“演示預(yù)覽成功”的場(chǎng)景只證明了第一層。第一層坦率說(shuō)沒(méi)什么說(shuō)服力因?yàn)橐粋€(gè)文本型的PDF幾十KB任何方案都能秒開(kāi)它。真正拉開(kāi)差距的是從第二層到第六層。用戶說(shuō)“預(yù)覽不了”往往不是真的白屏而是他看到第三頁(yè)的時(shí)候覺(jué)得太卡、他搜索一個(gè)關(guān)鍵詞搜不到、他打印出來(lái)排版亂了這些行為在他看來(lái)統(tǒng)稱“預(yù)覽不了”。我在內(nèi)部評(píng)審時(shí)反復(fù)講一個(gè)觀點(diǎn)如果你們的產(chǎn)品定義里只有“能顯示第一頁(yè)”那用戶上傳八百頁(yè)掃描件后卡死就不能怪用戶文件太大只能怪需求定義太淺。反過(guò)來(lái)如果你在需求階段就把預(yù)覽質(zhì)量分層寫(xiě)清楚技術(shù)選型和性能預(yù)算都會(huì)明確很多。3. 一套扛得住真實(shí)世界的PDF導(dǎo)入預(yù)覽方案3.1 文件拾取與類(lèi)型識(shí)別先別急著渲染拿到文件后第一件事不是生成預(yù)覽URL而是做類(lèi)型校驗(yàn)。別只信file.type也別只信擴(kuò)展名我建議兩層校驗(yàn)一起做一層是MIME白名單一層是魔數(shù)校驗(yàn)。PDF文件的文件頭固定是%PDF-這五個(gè)字符對(duì)應(yīng)十六進(jìn)制是25 50 44 46 2D。讀取文件前幾個(gè)字節(jié)判斷魔數(shù)比擴(kuò)展名可靠得多。下面這段是我常用的校驗(yàn)邏輯直接復(fù)制就能用const input document.getElementById(pdfInput); input.addEventListener(change, (e) { const file e.target.files[0]; if (!file) return; // 第一層擴(kuò)展名/MIME 快速過(guò)濾攔截明顯不是 PDF 的文件 const isPdfByMime file.type application/pdf; const isPdfByName /\.pdf$/i.test(file.name); const isPdfByMimeWithText /application\/octet-stream/.test(file.type) /\.pdf$/i.test(file.name); if (!isPdfByMime !isPdfByName !isPdfByMimeWithText) { alert(請(qǐng)選擇 PDF 文件); return; } // 第二層讀文件頭魔數(shù)只有前五個(gè)字節(jié)是 %PDF- 才繼續(xù) const reader new FileReader(); reader.onload (ev) { const bytes new Uint8Array(ev.target.result); if (bytes[0] ! 0x25 || bytes[1] ! 0x50 || bytes[2] ! 0x44 || bytes[3] ! 0x46 || bytes[4] ! 0x2D) { alert(文件頭不對(duì)這不像一個(gè)有效的 PDF 文件); return; } // 校驗(yàn)通過(guò)再走預(yù)覽邏輯 previewPdf(file); }; reader.readAsArrayBuffer(file.slice(0, 5)); });第一層MIME過(guò)濾會(huì)放走一部分“類(lèi)型是octet-stream但擴(kuò)展名是pdf”的文件這正是真實(shí)場(chǎng)景里最常見(jiàn)的合法情況第二層魔數(shù)校驗(yàn)會(huì)把那些擴(kuò)展名是pdf、內(nèi)容卻完全不是PDF的假文件攔下來(lái)。比那些只依賴accept.pdf的實(shí)現(xiàn)健壯性高一個(gè)量級(jí)。3.2 預(yù)覽方案怎么選iframe、embed還是PDF.js類(lèi)型校驗(yàn)過(guò)了接下來(lái)是選預(yù)覽渲染方案。我只說(shuō)三種主流的各有利弊方案優(yōu)點(diǎn)缺點(diǎn)適合場(chǎng)景iframe / embed / object 標(biāo)簽實(shí)現(xiàn)簡(jiǎn)單瀏覽器原生支持渲染行為完全交給瀏覽器內(nèi)核樣式不可定制不同內(nèi)核差異大內(nèi)網(wǎng)系統(tǒng)、簡(jiǎn)單展示、對(duì)交互無(wú)要求PDF.js渲染可控支持翻頁(yè)、縮放、搜索、打印自定義集成復(fù)雜需要配置worker部分特殊PDF特性支持不完美Web端正式產(chǎn)品、需要深度交互系統(tǒng)原生組件 / WKWebView移動(dòng)端體驗(yàn)好內(nèi)存由系統(tǒng)托管平臺(tái)差異大WebView版本會(huì)影響行為鴻蒙、Android、iOS原生App如果一個(gè)產(chǎn)品對(duì)預(yù)覽的定位是“用戶能看個(gè)大概”iframe方案省錢(qián)省事但如果產(chǎn)品定位是“用戶要在系統(tǒng)里完成審閱、標(biāo)注、搜索”P(pán)DF.js幾乎是目前Web端唯一能扛的方案。注意PDF.js不是簡(jiǎn)單的npm install就完事它的坑集中在worker配置上。3.3 為什么你的PDF.js總是白屏worker配置與渲染參數(shù)PDF.js默認(rèn)跑在Web Worker里解析PDF然后把渲染結(jié)果繪制到Canvas上。worker需要單獨(dú)加載一個(gè)文件如果你沒(méi)有正確設(shè)置workerSrc控制臺(tái)會(huì)出現(xiàn)類(lèi)似Setting up fake worker failed的報(bào)錯(cuò)頁(yè)面就是一片白。很多人的“預(yù)覽成功”演示之所以順利是因?yàn)閐emo環(huán)境里網(wǎng)絡(luò)暢通、CDN可用一旦部署到內(nèi)網(wǎng)CDN被禁用worker加載失敗預(yù)覽立刻失效。正確的做法是把pdf.worker.min.js下載到本地和你的業(yè)務(wù)代碼一起部署并用版本一致的路徑配置import * as pdfjsLib from pdfjs-dist; pdfjsLib.GlobalWorkerOptions.workerSrc ${import.meta.env.BASE_URL}assets/pdf.worker.min.js; const loadingTask pdfjsLib.getDocument({ data: arrayBuffer }); const pdf await loadingTask.promise; // 大文件場(chǎng)景建議配置這些 const page await pdf.getPage(1); const viewport page.getViewport({ scale: 1.4 });兩個(gè)容易踩的性能坑一是scale不要一味調(diào)高普通屏1.5足夠視網(wǎng)膜屏2.0也夠了過(guò)高的scale會(huì)讓Canvas內(nèi)存開(kāi)銷(xiāo)成倍上漲二是對(duì)于千頁(yè)大文件不要一次性把全部頁(yè)面渲染出來(lái)按當(dāng)前可視區(qū)域懶加載滾動(dòng)到哪頁(yè)渲染哪頁(yè)配合銷(xiāo)毀遠(yuǎn)處的頁(yè)面Canvas內(nèi)存才能壓得住。另外在一些老舊的WebView內(nèi)核里PDF.js的某些特性會(huì)觸發(fā)兼容問(wèn)題。如果線上反饋白屏優(yōu)先嘗試在初始化參數(shù)里加上isEvalSupported: false這條能讓不少低版本瀏覽器死里逃生。3.4 資源生命周期Object URL用完不釋放會(huì)出事使用Blob URLURL.createObjectURL是預(yù)覽大文件相對(duì)穩(wěn)妥的方式它不需要把文件讀成字符串而是交給瀏覽器內(nèi)部管理。但很多人在對(duì)接文件預(yù)覽時(shí)漏了另一半U(xiǎn)RL.revokeObjectURL。我見(jiàn)過(guò)一個(gè)真實(shí)案例某系統(tǒng)里連續(xù)打開(kāi)十幾個(gè)PDF之后瀏覽器占用從幾百M(fèi)B漲到2GB最后整臺(tái)機(jī)器卡死復(fù)現(xiàn)之后定位到問(wèn)題就是Object URL只創(chuàng)建、不回收。Blob URL底層持有整個(gè)文件的引用你不調(diào)用revoke瀏覽器就永遠(yuǎn)不能釋放這塊內(nèi)存。文件小的時(shí)候無(wú)所謂文件一大幾次操作就把頁(yè)面拖垮了。建議封裝一個(gè)統(tǒng)一的資源管理器class PdfResourceManager { constructor() { this.objectUrls new Set(); } create(file) { const url URL.createObjectURL(file); this.objectUrls.add(url); return url; } release(url) { if (this.objectUrls.has(url)) { URL.revokeObjectURL(url); this.objectUrls.delete(url); } } releaseAll() { this.objectUrls.forEach((url) URL.revokeObjectURL(url)); this.objectUrls.clear(); } }頁(yè)面卸載、組件銷(xiāo)毀、用戶切換文件、導(dǎo)入新文件時(shí)都要調(diào)用releaseAll()或者release()釋放舊資源。在iframe里預(yù)覽的時(shí)候還要注意一個(gè)細(xì)節(jié)先把iframe的src置空然后再revoke順序反了會(huì)導(dǎo)致當(dāng)前顯示的預(yù)覽立刻白屏。4. 從用戶高頻吐槽反推的排查速查表4.1 高頻問(wèn)題的現(xiàn)象與處置我把這些年遇到過(guò)的、網(wǎng)絡(luò)上高頻吐槽的PDF預(yù)覽問(wèn)題整理成了一張排查表。建議直接截圖保存遇到問(wèn)題按表自查?,F(xiàn)象可能原因排查思路/解決方案資源管理器里PDF縮略圖不顯示Adobe預(yù)覽處理器注冊(cè)損壞或系統(tǒng)預(yù)覽服務(wù)異常修復(fù)預(yù)覽處理器注冊(cè)表項(xiàng)重裝Adobe相關(guān)組件或改用系統(tǒng)自帶PDF預(yù)覽組件Win10/Win11資源管理器預(yù)覽窗格打開(kāi)很卡預(yù)覽窗格對(duì)每個(gè)文件都會(huì)調(diào)用處理器解析大PDF開(kāi)銷(xiāo)高在文件夾選項(xiàng)里關(guān)閉預(yù)覽窗格產(chǎn)品側(cè)縮小縮略圖生成尺寸避免全尺寸渲染Mac上空格選中PDF預(yù)覽空白Quick Look緩存損壞打開(kāi)終端執(zhí)行qlmanage -r cache或重啟quicklookd進(jìn)程某銀行類(lèi)App在鴻蒙手機(jī)上打開(kāi)PDF卡死WebView版本差異、大PDF在主線程渲染改用系統(tǒng)PDF組件或流式渲染限制同時(shí)渲染的頁(yè)數(shù)App內(nèi)打不開(kāi)預(yù)覽下載文件在系統(tǒng)里找不到應(yīng)用沙箱隔離了文件路徑使用系統(tǒng)文件選擇器或DocumentProvider共享文件不要自己拼接絕對(duì)路徑文件管理器里看到0KB或內(nèi)容亂碼文件下載被截?cái)嗷蚓幋a被破壞檢查Content-Length與實(shí)際下載字節(jié)數(shù)文件末尾必須存在%%EOFPDF打開(kāi)提示需要密碼文件被加密保護(hù)彈窗提示用戶輸入密碼且不要嘗試?yán)@過(guò)加密邏輯掃描版PDF搜索不到文字文件是純圖片沒(méi)有OCR文字層產(chǎn)品內(nèi)說(shuō)明限制或集成OCR能力后再搜索中文文件名亂碼文件名編碼處理不當(dāng)服務(wù)端響應(yīng)頭使用Content-Disposition并做URL編碼前端用decodeURIComponent回解打印出來(lái)的PDF排版錯(cuò)亂CSS頁(yè)面尺寸與PDF實(shí)際尺寸不一致或預(yù)覽層攔截了原打印流打印時(shí)用PDF原生尺寸輸出到iframe后調(diào)用打印傳打印參數(shù)這張表里排名靠前的每一個(gè)問(wèn)題幾乎都對(duì)應(yīng)著一句用戶的真實(shí)吐槽。你在網(wǎng)上搜“PDF預(yù)覽失敗”搜到的那些帖子不是個(gè)例是分布在各行各業(yè)的普遍遭遇。那些吐槽的帖子背后都是當(dāng)初在演示PPT里“預(yù)覽成功”的項(xiàng)目。4.2 發(fā)布前做好這套驗(yàn)收矩陣別只在演示環(huán)境過(guò)關(guān)如果你想把“別只演示預(yù)覽成功”落到實(shí)處最關(guān)鍵的動(dòng)作是建立一份驗(yàn)收矩陣。我們的做法是每次版本發(fā)布前拿一張對(duì)照表逐項(xiàng)過(guò)六十多個(gè)組合全部跑一遍才允許提測(cè)上線。維度必須覆蓋的測(cè)試項(xiàng)操作系統(tǒng)Windows 10、Windows 11、macOS、國(guó)產(chǎn)Linux桌面、鴻蒙、Android、iOS瀏覽器/內(nèi)核Chrome、Edge、Firefox、Safari、微信內(nèi)置WebView、鴻蒙WebView、安卓系統(tǒng)WebView文件類(lèi)型文字型PDF、掃描圖片型PDF、帶表單PDF、帶書(shū)簽PDF、加密PDF、舊版軟件生成的低版本PDF文件大小1MB以內(nèi)、10MB、100MB、300MB以上異常狀態(tài)損壞文件、0字節(jié)文件、擴(kuò)展名偽裝文件、文件名含中文和空格的文件網(wǎng)絡(luò)條件離線狀態(tài)、弱網(wǎng)狀態(tài)、CDN失效狀態(tài)、內(nèi)網(wǎng)無(wú)外網(wǎng)狀態(tài)每個(gè)組合測(cè)試時(shí)別只看第一頁(yè)截圖。實(shí)測(cè)項(xiàng)目至少包括打開(kāi)耗時(shí)、翻到第50頁(yè)的內(nèi)存占用、搜索能否命中、縮放是否掉幀、打印是否分頁(yè)正常、重復(fù)打開(kāi)十個(gè)文件后內(nèi)存是否回落。這些數(shù)據(jù)才算真正有效的驗(yàn)收結(jié)果。我曾經(jīng)發(fā)現(xiàn)一個(gè)很有意思的現(xiàn)象同一套PDF.js版本W(wǎng)indows提交的測(cè)試記錄一切正常macOS上翻頁(yè)偶爾閃爍鴻蒙WebView里搜索高亮失效。后來(lái)逐一排查發(fā)現(xiàn)是三個(gè)平臺(tái)對(duì)Canvas繪制寬高比的精度處理不同有的平臺(tái)要求顯式設(shè)置devicePixelRatio有的平臺(tái)對(duì)半透明色塊的合成算法不一致。這些問(wèn)題如果在演示環(huán)境里根本不會(huì)被發(fā)現(xiàn)因?yàn)檠菔局挥昧艘慌_(tái)電腦、一個(gè)瀏覽器、一個(gè)普通PDF。測(cè)試矩陣的價(jià)值就在這里——把個(gè)例變成覆蓋率。個(gè)人實(shí)操后的一點(diǎn)體會(huì)前幾年我每次做完P(guān)DF導(dǎo)入都會(huì)建一個(gè)“垃圾測(cè)試文件夾”里面放八百頁(yè)掃描件、密碼保護(hù)的合同、從舊系統(tǒng)導(dǎo)出的畸形PDF、文件名帶長(zhǎng)串亂碼的文件每次改動(dòng)后先用這批文件過(guò)一遍。這個(gè)習(xí)慣是從被線上故障教訓(xùn)過(guò)之后養(yǎng)成的。再后來(lái)我做演示的時(shí)候開(kāi)場(chǎng)白往往會(huì)加一句“預(yù)覽成功只是起點(diǎn)真正能扛住用戶手里那些奇奇怪怪的PDF才叫導(dǎo)入功能完成?!边@句話既是說(shuō)給在場(chǎng)的人聽(tīng)的也是說(shuō)給自己的。