ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Vue项目中实现PDF、Word、Excel在线预览的完整方案

Vue项目中实现PDF、Word、Excel在线预览的完整方案 1. 从需求到方案Vue里做文档预览其实是在做标准化最近好几个朋友问我在 Vue 项目里怎么预览 PDF、Word、Excel 文档说是后台管理系统里要支持在线看合同、看报表、看标书不能每次都让用户下载到本地再打开。这个需求太常见了我接过的 OA 类、ERP 类、低代码平台类项目里几乎都是标配功能。先说清楚这个需求的难点不在于“打开文件”而在于三点第一是不同格式文件的解析方式完全不同PDF 是一套生态Word 是一套生态Excel 又是另一套生态第二是要尽量保持原文件的排版和样式不能解析出来乱七八糟的第三是要在浏览器里跑不能强迫用户装 Office 或者 PDF 阅读器。实际上这三个格式里PDF 是最好解决的因为浏览器原生就能打开 PDF 文件只要给一个文件流地址浏览器自己就能渲染。麻烦的是 Word 和 Excel尤其是 Word老版本的 .doc 格式在新浏览器里几乎没法直接渲染而 .docx 虽然号称是国际标准但浏览器依然不认。Excel 更不用说一个工作簿可能有多个 Sheet、合并单元格、数据透视表、条件格式前端要想完整还原难度直接拉满。这就有个很现实的选型问题怎么在“保持格式”和“技术实现难度”之间找到平衡。下面我会把我实际用过的几套方案都梳理一遍从纯前端解析到后端转码再到第三方在线预览服务以及它们在 Vue 项目里怎么落地都写清楚。这篇文章适合所有在 Vue 项目里做文档管理、文件预览、知识库系统的前端开发者照着抄就能用。2. 技术选型拆解纯前端、后端转码还是第三方服务2.1 纯前端解析方案纯前端解析的意思是所有文件解析和渲染都在浏览器端完成前端拿到文件对象或者 URL 之后通过 JavaScript 库去解析文件内容并渲染到页面上。这种方式的好处是不需要后端参与没有额外部署成本预览速度也快文件不出服务器数据安全性也好。缺点是解析能力受制于前端库的成熟度遇到特别复杂的文件比如加密 PDF、宏文档、嵌套很深的老式表格就比较容易翻车。我这里把主流方案列一下文件类型推荐库/方案原理概括成熟度PDFpdf.jsMozilla 出品PDF 解析 Canvas 渲染极高PDFiframe / embed 标签浏览器内置 PDF 渲染器高DOCXdocx-preview解析 docx 内部 XML 结构并转 HTML中高XLSXSheetJSxlsx 社区版解析 Excel 文件为 JSON/HTML高纯前端方案里我要重点说一句PDF 用 pdf.js 基本不会有大坑Word 和 Excel 可以用但要有“复杂文件渲染不到位”的心理准备。2.2 后端转码 前端展示方案如果你们的系统是 Java 后端最常见的做法是后端先把 Office 文件转成 PDF前端只负责预览 PDF这样前端只需要处理一种格式所有 Word 和 Excel 的兼容性问题都丢给后端解决。后端转码的工具有 LibreOffice免费、Apache POI免费但复杂样式吃力、Aspose商业收费但转换效果最好、kkFileView开源项目、开箱即用。这个方案的优点显而易见前端逻辑简单、渲染效果好、兼容性极强。缺点也很明显需要后端配合开发接口高并发场景下转码会吃 CPU 和内存文件一多就得考虑队列和任务调度。2.3 第三方在线预览服务还有一类更省事的方案直接把文件 URL 拼到微软 Office Online 的预览服务上比如用https://view.officeapps.live.com/op/view.aspx?src文件地址它能在浏览器里渲染 Word 和 Excel。这种方式连代码都不用写几行把 URL 塞进 iframe 就行。但是第三方服务有一个绕不过去的坑它要求你的文件必须有一个公网可访问的 URL。内网系统、私有化部署的系统文件地址外部访问不到这条路就堵死了。另外预览效果受微软官方服务的限制用户可能要求对预览区域做定制这个方案也做不到。2.4 我在实际项目里的选择我一般遵循一个原则如果能接受“文件不走出服务器”优先纯前端方案毕竟部署简单如果文件格式很复杂、用户又要求百分百还原那直接走后端转码用 LibreOffice 转 PDF 再用 pdf.js 展示。如果只是给演示 demo 用不涉及内网环境才考虑第三方方案。下面我把每种方案在 Vue 里具体怎么实现都过一遍。3. PDF 预览用 pdfjs-dist 做浏览器内的完整阅读器3.1 为什么不用 iframe 直接打开 PDF最偷懒的方式是这样iframe :srcpdfUrl stylewidth: 100%; height: 800px;/iframe如果你的 PDF 文件是浏览器原生支持的版本这样确实能看。但实际项目中你会发现三个问题第一不同浏览器内置 PDF 阅读器的工具栏不一样Chrome 的工具栏会盖住一部分页面Firefox 的界面又完全是另一套用户体验不可控。第二iframe 打开的 PDF 无法定制交互比如你想做一个“签章确认”按钮、想在 PDF 上加自己的标注这些浏览器原生 PDF 阅读器都不支持。第三在移动端和某些 WebView 环境里浏览器可能根本没有内置 PDF 渲染能力直接白屏。所以真正要做系统化办公文档预览必须自己画一个 PDF 阅读器。pdf.js 是 Mozilla 开源的 PDF 解析渲染引擎Firefox 内置的 PDF 阅读器就是它做的可靠性和渲染精度都值得信任。它能通过 Canvas 把 PDF 每一页画出来这意味着我们可以在上面叠加任何 HTML 元素定制能力一下就打开了。3.2 pdfjs-dist 在 Vue 里的基础用法我以 Vue 3 Vite 为例。先安装依赖npm install pdfjs-dist需要注意版本差异。pdfjs-dist 从 v4 开始 ES Module 结构变化比较大老教程里都是import PDFJS from pdfjs-dist这种写法新版已经不行了。我写这篇的时候用的是 v4.x正确的引入方式是import * as pdfjsLib from pdfjs-dist // 指定 worker 文件路径这是关键一步 pdfjsLib.GlobalWorkerOptions.workerSrc new URL( pdfjs-dist/build/pdf.worker.min.mjs, import.meta.url ).toString()然后封装一个预览组件核心流程是读取文件源 - 加载 PDF 文档 - 逐页渲染到 Canvastemplate div classpdf-container div v-forpage in pages :keypage classpdf-page canvas :idpdf-canvas-${page}/canvas /div /div /template script setup import { ref, onMounted, watch } from vue import * as pdfjsLib from pdfjs-dist const props defineProps({ url: { type: String, required: true } }) const pages ref([]) const scale 1.5 // 缩放比例控制清晰度 // 渲染指定页码 async function renderPage(pdf, pageNumber) { const page await pdf.getPage(pageNumber) const viewport page.getViewport({ scale }) const canvas document.getElementById(pdf-canvas-${pageNumber}) const context canvas.getContext(2d) canvas.width viewport.width canvas.height viewport.height await page.render({ canvasContext: context, viewport }).promise } onMounted(async () { const loadingTask pdfjsLib.getDocument(props.url) const pdf await loadingTask.promise pages.value Array.from({ length: pdf.numPages }, (_, i) i 1) // 等待 DOM 渲染完再逐页绘制 await nextTick() for (let i 1; i pdf.numPages; i) { await renderPage(pdf, i) } }) /script这里有几个坑我要特别说第一个是 Worker 路径。很多新人会漏掉GlobalWorkerOptions.workerSrc这一行结果运行时报Failed to fetch dynamically imported module或者Setting up fake worker failed。原理是 pdf.js 解析和渲染 PDF 的逻辑如果不放 Worker 里会在主线程跑界面会直接卡死。Vite 里用new URL(...)的方式让打包工具正确识别这个 worker 文件并输出到可访问的路径。第二个是 Canvas 尺寸。你直接设置canvas.width viewport.width会发现在高分屏Retina 屏上文字是模糊的。更好的做法是根据window.devicePixelRatio做放大const ratio window.devicePixelRatio || 1 canvas.width viewport.width * ratio canvas.height viewport.height * ratio canvas.style.width viewport.width px canvas.style.height viewport.height px context.scale(ratio, ratio)3.3 PDF 预览组件的进阶功能翻页、缩放、打印能做出来是一回事能用是另一回事。真正的业务系统里用户会需要翻页、放大缩小、甚至打印。这里我给大家一个做翻页和缩放的思路// 当前页码 const currentPage ref(1) // 缩放等级 const currentScale ref(1.5) async function loadPage() { const page await pdfDoc.getPage(currentPage.value) const viewport page.getViewport({ scale: currentScale.value }) const canvas canvasRef.value const context canvas.getContext(2d) canvas.width viewport.width canvas.height viewport.height await page.render({ canvasContext: context, viewport }).promise } function nextPage() { if (currentPage.value pdfDoc.numPages) { currentPage.value loadPage() } }打印方面我用的是一个很实用的办法把已经渲染出来的 Canvas 页面依次交给浏览器打印接口逐页打印function print() { const printWindow window.open(, _blank) printWindow.document.write(htmlheadtitle打印PDF/title/headbody) pages.value.forEach((_, i) { const canvas document.getElementById(pdf-canvas-${i 1}) printWindow.document.write(img src${canvas.toDataURL(image/png)} /) }) printWindow.document.write(/body/html) printWindow.document.close() printWindow.print() }这个方法我用了很多年在绝大多数浏览器上都稳定唯一的缺点是打印图片而不是矢量内容清晰度有限。但对内部系统来说已经足够。4. Word 预览docx-preview 还原排版和样式4.1 .docx 的底层结构和渲染原理接触 Word 预览之前我先带你理解一个关键点.docx 文件本质上是一个 ZIP 压缩包里面装着一堆 XML 文件其中word/document.xml存的是正文内容word/media/目录存的是图片word/styles.xml存的是样式定义。所以前端预览 Word 的核心思路就是解压这个 ZIP读取里面的 XML 和媒体文件再渲染成 HTML。docx-preview 这个库就是干这个事的它内部自动完成了 ZIP 解压、XML 解析、样式映射和 DOM 渲染。它的渲染效果比简单的把 XML 转 HTML 好很多因为对段落样式、分页、表格、页眉页脚、图片浮动都有处理。4.2 docx-preview 的 Vue 封装实战安装依赖npm install docx-preview组件里这样写template div refcontainerRef classword-preview/div /template script setup import { ref, onMounted, watch } from vue import { renderAsync } from docx-preview const props defineProps({ url: { type: String, required: true } }) const containerRef ref(null) async function preview() { const response await fetch(props.url) const blob await response.blob() // renderAsync 接收 blob 或 arrayBuffer await renderAsync(blob, containerRef.value, undefined, { className: docx, inWrapper: true, ignoreWidth: false, ignoreHeight: false, ignoreFonts: false, breakPages: true, experimental: false }) } onMounted(() { preview() }) /script这里renderAsync的参数里breakPages: true表示按分页符拆分页面这样在页面上展示时接近 Word 里的分页效果ignoreWidth和ignoreHeight默认是 false意思是保留文档中定义的页面宽度和高度这样能最大限度还原原排版。4.3 遇到 .doc 老格式怎么办上面的方案只能处理 .docx如果是老的 .doc 格式docx-preview 是打不开的因为 .doc 是二进制复合文档格式和 XML 完全不是一回事。这时候有两种思路第一种如果用户手里就是 .doc 文件前端可以先做一个扩展名校验遇到 .doc 时给出提示在产品层面告知“老版本文档请先另存为 .docx 再上传”。这个方法有点粗暴但对很多内部系统来说其实是最实用、最省事的。第二种后端用 LibreOffice 做一个在线转换接口用户上传 .doc 时后端起命令行soffice --headless --convert-to docx --outdir /tmp/convert /tmp/upload/old.doc转换完成后再把 .docx 返回给前端用 docx-preview 渲染。这个方案对用户最友好而且 LibreOffice 是开源免费的转换效果在绝大多数场景下都够用。4.4 Word 预览的样式还原坑用 docx-preview 跑起来不难但真正投入生产前你最好有一个“样式崩溃”的心理预期。我踩过的坑主要有这么几个中文字体问题。如果文档里用了本机没有的字体浏览器会用默认字体替代排版会变得和原文件不一样。如果后端转 PDF 预览就不存在这个问题因为 LibreOffice 转 PDF 时会做字体嵌入。但纯前端 docx-preview 方案里想保留字体需要你在前端把对应的字体文件比如 woff2加载到浏览器里用 CSS 声明 font-facedocx-preview 会尽量去匹配。页眉页脚和页码。docx-preview 对 page margin 的处理基本 OK但涉及分节符、奇偶页不同的页眉页脚这类复杂功能渲染出来偶尔会出现错位。表格宽度自适应。有些从网页直接复制进 Word 的表格宽度可能是固定值而有些设置了百分比。docx-preview 会按文档里的属性渲染但如果容器比页面宽度窄表格可能会溢出。我一般会在组件外层加一个 CSS 规则.word-preview table { table-layout: fixed; word-break: break-word; max-width: 100%; }5. Excel 预览SheetJS 解析数据、渲染成表格5.1 用 SheetJS 把 Excel 读成 JSON 再渲染说到 Excel 预览第一个想到的就是 SheetJSnpm 包名是 xlsx。它是目前 JavaScript 生态里最成熟的 Excel 解析库社区版就能解析 xlsx、xls、csv 等格式读取每个 Sheet 的单元格数据、合并单元格信息、行列数量。拿到这些数据之后剩下的就是前端表格渲染的活了。安装和基本用法npm install xlsximport * as XLSX from xlsx async function parseExcel(file) { const data await file.arrayBuffer() const workbook XLSX.read(data, { type: array }) // 取第一个 Sheet const firstSheetName workbook.SheetNames[0] const sheet workbook.Sheets[firstSheetName] // 转为二维数组的数据结构 const rows XLSX.utils.sheet_to_json(sheet, { header: 1 }) // 也可以直接转 HTML 表格但样式有限 const htmlTable XLSX.utils.sheet_to_html(sheet) console.log(rows, htmlTable) }这里header: 1的意思是返回二维数组每一行是一个数组单元格内容按位置排列。如果你不传这个参数默认会根据第一行的表头字段生成对象数组某些场景反而更好用。5.2 渲染层怎么选自己写表格还是用现成组件拿到 rows 数组之后最简单的做法是直接渲染成一个 HTML 表格template table classexcel-preview-table v-ifrows.length tr v-for(row, rowIndex) in rows :keyrowIndex td v-for(cell, cellIndex) in row :keycellIndex{{ cell }}/td /tr /table /template这种方法的优点是无依赖、渲染快缺点是复杂文件的处理能力很弱。合并单元格不会自动处理日期会变成一串数字Excel 内部存储的序列值数字精度也可能展示不对。想要更好的效果我有两个建议第一个建议是采用 HTMLtable手动处理合并单元格。SheetJS 的sheet[!merges]属性里是一个合并单元格区域的数组每个元素形如{s: {r:0, c:0}, e: {r:0, c:2}}表示从第 0 行第 0 列到第 0 行第 2 列合并。在渲染时遍历这个数组借助rowspan和colspan实现合并效果。第二个建议是直接用现成的表格组件比如vxe-table或者ag-grid把 SheetJS 解析出来的数据填充进去。这些组件自带虚拟滚动几千行数据也不会卡还支持列宽拖拽。我个人的经验是如果 Excel 文件超过 500 行自己写 HTML 表格就会出现性能问题这时候用vxe-table更合适。5.3 Excel 预览的复杂场景处理多 Sheet 和公式大型企业的 Excel 文件通常有多个 Sheet比如一个预算表Sheet1 是汇总Sheet2 是明细。预览组件里应该做一个 Sheet 切换的 Tab 栏const sheetNames workbook.SheetNames const currentSheetName ref(sheetNames[0]) watch(currentSheetName, () { const sheet workbook.Sheets[currentSheetName.value] rows.value XLSX.utils.sheet_to_json(sheet, { header: 1 }) })还有一个容易被忽略的问题公式。SheetJS 社区版在读取带有公式的单元格时返回的是公式字符串比如SUM(A1:A10)而不是计算结果。如果用户要预览的是一个带大量公式的报表页面上就会显示一坨公式而不是数字。此时有两个选择一是使用 SheetJS 的商业版它带计算引擎二是后端用 Apache POI 或 LibreOffice 把 Excel 转成 PDF前端直接预览 PDF彻底绕开这个问题。我在实际项目里遇到需要“逼真还原”的 Excel 预览需求最后的方案基本都是“Excel 转 PDF”而不是“前端解析 Excel”。因为在金融、财务类系统里用户对数字的要求是“所见即所得”任何解析偏差都会引发信任问题。5.4 用 iframe 嵌入 Office Online 实现零成本 Excel 预览如果你们的 Excel 文件具备公网访问条件还有一个零代码方案我可以顺手说一下。微软的 Office Apps 提供了在线预览接口iframe srchttps://view.officeapps.live.com/op/view.aspx?srchttps%3A%2F%2Fyourdomain.com%2Ffile.xlsx width100% height800px /iframe只要把文件地址做 URL 编码后拼到 src 后面它就能在 iframe 里渲染出接近 Office 的效果还能支持基本的操作。这个方案对 Word 也管用。但我要提醒一句这个接口只对“可以直接访问的 URL”生效而且每次预览都要把文件上传到微软的服务端中转。内部系统或者对数据安全有要求的系统千万别用。6. 统一预览入口一个组件搞定三类文件6.1 按文件类型分发渲染器业务系统里不会单独只预览 PDF 或只预览 Excel通常是一个文件列表里有三种文件混在一起。所以在组件设计上强烈建议封装一个FilePreview.vue根据文件后缀分发到不同的渲染器。核心逻辑大概是template div classfile-preview PdfViewer v-iftype pdf :urlfileUrl / WordViewer v-else-iftype doc || type docx :urlfileUrl / ExcelViewer v-else-iftype xls || type xlsx :urlfileUrl / ImagePreview v-else-ifisImage(type) :urlfileUrl / div v-else暂不支持该文件类型预览请下载后查看/div /div /template script setup import { computed } from vue const props defineProps({ fileUrl: { type: String, required: true }, fileName: { type: String, required: true } }) const type computed(() { const ext props.fileName.split(.).pop().toLowerCase() if ([pdf].includes(ext)) return pdf if ([doc, docx].includes(ext)) return word if ([xls, xlsx, csv].includes(ext)) return excel if ([png, jpg, jpeg, gif].includes(ext)) return image return unknown }) /script组件拆分的好处是各渲染器之间互不影响PDF 的逻辑不会污染 Excel 的逻辑维护起来很清晰。6.2 文件流地址的传递规范这里我要单独强调一个非常关键的细节文件预览接口的返回方式。很多后端的文件下载接口返回的是application/octet-stream或者附带了Content-Disposition: attachment响应头。浏览器一旦遇到这个头就会触发下载而不是直接读取。所以预览接口必须满足两个条件之一返回类型是浏览器能直接渲染的 MIME 类型比如application/pdf或者后端使用内联渲染头Content-Disposition: inline如果你的后端接口不是这样前端用 fetch 拉取文件流时拿到的 blob 对象依然可以喂给 pdf.js 和 docx-preview因为它们都支持 blob/arrayBuffer 输入。但如果直接用 iframe 加载这个 URL就可能变成下载而不是预览。我习惯的做法是统一封装一个 getFileBlob 函数所有预览组件都从 blob 开始export async function getFileBlob(fileUrl) { const response await fetch(fileUrl) if (!response.ok) { throw new Error(文件加载失败 response.status) } return response.blob() }拿到 blob 之后如果是 PDF可以用URL.createObjectURL(blob)生成一个临时 URL再传给 pdf.js 或 iframe如果是 Word直接传给 docx-preview如果是 Excel用blob.arrayBuffer()传给 SheetJS。6.3 预览组件的状态管理加载中、失败、空状态做前端不能只考虑“成功路径”。文件预览是 IO 密集型操作网络慢、文件大、格式异常都可能发生所以组件里至少要有三态template div classpreview-container div v-ifloading classpreview-loading文件加载中.../div div v-else-iferror classpreview-error {{ errorMsg }} button clickretry重试/button /div slot v-else / /div /template script setup import { ref } from vue import { getFileBlob } from /utils/file const props defineProps({ fileUrl: { type: String, required: true } }) const loading ref(true) const error ref(false) const errorMsg ref() async function load() { loading.value true error.value false try { const blob await getFileBlob(props.fileUrl) // 这里根据文件类型分发渲染 loading.value false } catch (e) { error.value true errorMsg.value e.message || 预览失败 loading.value false } } load() /script尤其要注意文件预览失败时界面不能白屏一定要给用户一个明确的“重试”入口。很多内部系统的用户年龄跨度大遇到白屏第一反应是刷新整个页面体验很差。7. 常见问题与排查技巧实录7.1 pdf.js 跨域加载失败pdf.js 的getDocument方法底层是 fetch所以它受浏览器的跨域限制。如果你把 PDF 文件放在 OSS 或者其他域名上前端用 pdf.js 直接加载会报跨域错误。解决办法有三个一是给 OSS 配置 CORS推荐一劳永逸二是前端先用 fetch 拿 blob再喂给 pdf.js前提是 fetch 本身也要跨域通过但 fetch 可以配合代理解决三是开发环境下配置 Vite 代理// vite.config.js export default { server: { proxy: { /pdf: { target: https://your-oss-domain.com, changeOrigin: true } } } }7.2 docx-preview 渲染出来只有一个空白页面这个坑我排查了很久才找到原因用的是arrayBuffer传参。docx-preview 的renderAsync第一参数接受 Blob 或 ArrayBuffer但如果你是从 response 里取数据直接response.arrayBuffer()再传给 renderAsync有时候会渲染空白。换成 Blob 就正常了。而且最好在组件销毁时清空容器onBeforeUnmount(() { if (containerRef.value) { containerRef.value.innerHTML } })7.3 Excel 数值显示不正常日期变成 45123SheetJS 解析日期时如果没有提前格式化返回的是 Excel 内部序列号。这时候可以用cellDates: true选项XLSX.read(data, { type: array, cellDates: true })这样日期类型会转为 JavaScript 的 Date 对象。如果还不行手动格式化function formatCellValue(cell) { if (cell.t d) { return cell.v.toLocaleDateString() } return cell.v }7.4 大文件预览卡死浏览器几十 MB 的 PDF 和 Excel 文件确实能把浏览器拖垮。我的处理方式是PDF 使用延迟渲染滚动到哪页渲染哪页类似动态懒加载Excel 预览限制展示行数超过 2000 行直接提示用户转 PDF 或用专业软件查看Word 文件超过 20MB 直接走后端转 PDF 方案不硬刚给你贴一段 PDF 懒加载的核心思路// 监听滚动容器判断页面是否进入可视区 container.addEventListener(scroll, () { const rect container.getBoundingClientRect() pages.value.forEach((_, index) { const pageEl document.getElementById(pdf-page-${index 1}) const pageRect pageEl.getBoundingClientRect() // 如果页面进入了可视区 200px 范围内且还没渲染就触发渲染 if (pageRect.top rect.bottom 200 pageRect.bottom rect.top - 200 !renderedPages.has(index)) { renderPage(pdf, index 1) renderedPages.add(index) } }) })7.5 统一预览组件的性能优化思路最后聊一下整体性能。我们项目里预览组件接入的文件都是从后端接口动态拉的文件少则几 MB多则上百 MB。我做的优化核心是“能缓存就缓存”PDF 和 Word 转出来的 Blob 都放进浏览器内存缓存里用 Map 维护同一个文件第二次打开时直接走缓存避免重复请求。const fileCache new Map() export async function getFileWithCache(fileUrl) { if (fileCache.has(fileUrl)) { return fileCache.get(fileUrl) } const blob await getFileBlob(fileUrl) fileCache.set(fileUrl, blob) return blob }如果文件很大可以用浏览器的 Cache API 做持久化缓存刷新页面后依然有效。8. 写在最后的经验和建议文档预览这个功能表面看是技术问题本质上是用户预期管理问题。我做了这么多年的前端最大的感受是不要试图在前端解决所有格式的所有细节而是要设计一个合理的降级链路。在我的项目里最终采用的组合方案是这样的PDF 用 pdf.js 自研阅读器Word 用 docx-preview 纯前端渲染Excel 优先走后端转 PDF。这三个方案在成本和效果之间找到了我目前认为最合适的平衡点。如果你是从零开始做我建议你先不要一步到位去追求完美还原而是先把三种格式的基础预览跑通再逐步补功能。具体顺序是先做 PDF因为它最简单、最容易出效果再做 Excel因为它结构相对规则最后啃 Word因为它的排版实在太多变。最后再分享一个小技巧不管用哪种方案预览组件的加载速度都很影响用户体验。建议在预览接口返回文件流之前先用 HTTP HEAD 请求拿到文件大小如果文件超过 10MB就在界面上提示“文件较大加载可能需要几秒钟”而不是让用户盯着空白屏幕干等。这个细节很小但用户感知非常明显用了之后团队的吐槽率直线下降。
返回列表