PDF 预览和下载你是怎么实现的?(下)

简介: PDF 预览和下载你是怎么实现的?

避免踩一些坑

坑一:不推荐 pdf-image

在实现服务端将 pdf 文件转换成图片时需要依赖到一些第三方包,一开始使用了 pdf-image 这个包,但在实际转换时发生较多的异常错误,顺着错误查看源码后发现其内部需要依赖一些额外的工具,因为其中需要使用 pdfinfo xxx 相关命令,并且其对应的 issue 上也存在着一些类似问题,但都试了试最后还是没有成功!

image.png

因此,更推荐使用 pdf-poppler 其中附带了一个 pdftocairo 的程序可以实现 pdf 到 图片 的转换能力,不过它目前版本支持 WindowsMac OS,如下:

image.png

坑二:path.basename not a function

在上述的代码内容中需要获取文件的名称,实际上我们可以简单直接的使用 Node Apipath.basename(path[, suffix]) 来达到目的:

image.png

但是在程序运行时发生了如下 异常,对应的 代码内容 和 运行结果 如下:

// 配置参数
  const options = {
    format: 'png',
    out_dir: dir,
    out_prefix: path.baseName(filePath, path.extname(filePath)), // 发生异常
    page: null,
  }
复制代码

image.png

这个暂时没有找到是什么原因,只能自己简单实现了一个 getFileName 方法用于获取文件的名称。

报错原因:太依赖编辑器的自动提示,将 basename 输出成 baseName ,没错就是 n 和 N 的区别.

坑三:细节

上述内容通过 koa 启动模拟业务服务,由于 业务服务(http://127.0.0.1:4000应用服务 (http://127.0.0.1:3000) 间的端口不一致,因此会产生 跨域,此时可以通过 koa-cors 来解决,值得注意的是有时候的那个业务服务器重启时 koa-cors 可能不起作用。

由于响应的内容直接在 koa 通用中间件中返回,因此,如果你需要支持业务服务提供 静态资源 的访问能力,就可以通过 koa-static 来实现,值得注意的是,当你通过 koa-static 指定静态文件资源后,如 app.use(koaStatic('./static')),此时如果你直接通过 http://127.0.0.1:4000/static/pdf/xxx.png 时,那么会得到 404 Not Found 的错误,原因在于 koa-static 是直接把 /static/ 设置成了 根路径,因此正确的访问路径为:http://127.0.0.1:4000/pdf/xxx.png

效果演示

发票内容不方便展示这里就不直接展示了,只需要关注生成的图片和路径即可:

image.png

PDF 下载

这里的下载实际不仅指 pdf 的下载,而是客户端方面所能支持的下载方式,最常见的如下几种:

  • a 标签,例如 <a href="xxxx" download="xxx">下载</a>
  • location.href,例如 window.location.href = xxx
  • window.open,例如 window.open(xxx)
  • Content-disposition,例如 Content-disposition:attachment;filename="xxx"

<a> 实现下载

<a>download 属性用于指示浏览器 下载 href 指定的 URL,而不是导航到该资源,通常会提示用户将其保存为本地文件,如果 download 属性有指定内容,这个值就会在下载保存过程中作为 预填充的文件名,主要是因为如下原因:

  • 这个值可能会通过 JavaScript 进行动态修改
  • 或者 Content-Disposition 中指定的 download 属性优先级高于 a.download

这种应该是大家最熟悉的方式了,但熟悉归熟悉,还有一些值得注意的点:

  • download 属性只适用于 同源 URL
  • 同源 URL 会进行 下载 操作
  • 非同源 URL 会进行 导航 操作
  • 非同源的资源 仍需要进行下载,那么可以将其转换为 blob: URLdata: URL 形式
  • HTTP 响应头中的 Content-Disposition 属性中指定了一个不同的文件名,那么会优先使用 Content-Disposition 中的内容
  • HTTP 若 HTTP 响应头中的 Content-Disposition  被设置为 Content-Disposition='inline',那么在 Firefox 中会优先使用 Content-Dispositiondownload 属性

静态方式:

<a href="http://127.0.0.1:4000/pdf/2-1.png" download="2.pdf">下载</a>
复制代码

动态方式:

function download(url, filename){
  const a = document.createElement("a"); // 创建 a 标签
  a.href = url; // 下载路径
  a.download = filename;  // 下载属性,文件名
  a.style.display = "none"; // 不可见
  document.body.appendChild(a); // 挂载
  a.click(); // 触发点击事件
  document.body.removeChild(a); // 移除
}
复制代码

Blob 方式

if (reqConf.responseType == 'blob') {
    // 返回文件名
    let contentDisposition = config.headers['content-disposition'];
    if (!contentDisposition) {
      contentDisposition = `;filename=${decodeURI(config.headers.filename)}`;
    }
    const fileName = window.decodeURI(contentDisposition.split(`filename=`)[1]);
    // 文件类型
    const suffix = fileName.split('.')[1];
    // 创建 blob 对象
    const blob = new Blob([config.data], {
      type: FileType[suffix],
    });
    const link = document.createElement('a');
    link.style.display = 'none';
    link.href = URL.createObjectURL(blob); // 创建 url 对象
    link.download = fileName; // 下载后文件名
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link); // 移除隐藏的 a 标签 
    URL.revokeObjectURL(link.href); // 销毁 url 对象
  }
复制代码

Content-dispositionlocation.href/window.open 实现下载

这看似是三种下载方式,但实际上就是一种,而且还是以 Content-disposition 为准。

Content-Disposition 响应头 指示回复的内容该以何种形式展示,是以 内联 的形式(即网页或页面的一部分)展示,还是以 附件 的形式 下载 并保存到本地,如下:

  • inline: 是 默认值,表示回复中的消息体会以页面的一部分或者整个页面的形式展示
Content-Disposition: inline
复制代码
  • attachment: 设置为此值意味着消息体应该被下载到本地,大多数浏览器会呈现一个 "保存为" 的对话框,并将 filename 的值预填为下载后的文件名
Content-Disposition: attachment; filename="filename.jpg"
复制代码

因此,基于 location.href='xxx'window.open(xxx) 的方式能实现下载就是基于 Content-Disposition: attachment; filename="filename.jpg" 的形式,又或者说是触发了浏览器本身的下载行为,满足了这个条件,无论是通过 a 标签跳转location.href 导航window.open 打开新页面直接在地址栏上输入 URL 等都可以实现下载。

H5 移动端的下载

H5 移动端针对于 预览 操作而言基于以上的方式都是可以实现,但是 下载 操作可就不同了,因为这是要区分场景:

  • 基于 手机浏览器
  • 基于 微信内置浏览器

基于 手机浏览器 的下载方式和上述提到的内容大致上也是一致的,本质上只要所在的客户端支持下载那就没有问题,然而在 微信内置浏览器 中你使用常规的下载方式可能达不到预期:

  • Android 中使用常规的下载方式,通常会弹出对话框,询问你是否需要唤醒 手机浏览器 来实现对应资源的下载,部分机型却不会
  • IOS 中以上方式都 无法实现下载,因此通常情况下会打开一个新的 webview 来提供预览,部分机型在新的页面中支持 长按屏幕 的方式进行保存操作,但并不是所有机型都支持

本质原因是在 微信内置浏览器 中屏蔽任何的 下载链接,如 APP 的下载链接普通文件 的下载链接 等等。

H5 移动端的下载还能怎么做?

由于这是 微信内置浏览器 环境对下载功能的屏蔽,因此 不用再考虑(想都不敢想)基于 微信内置浏览器 来实现下载功能,转而应该考虑的是如何实现 间接下载

  • 判断当前是否是属于 微信内置浏览器,若是则帮助用户自动唤起 手机浏览器 实现下载,但并不是所有机型都支持 唤起 操作,因此最好是提示使用用户直接通过 手机浏览器 实现下载,为了方便用户,可以实现 一键复制 的功能进行辅助
  • 另一种就直接提示只支持 PC 端下载,放弃对移动端的下载操作

最后

综上所述,实际在实现 pdf 预览的过程中可能暂时没有办法达到完美的方式,特别是针对类似 发票类pdf 文件,仍存在如下的问题:

  • 无法保证 h5 移动端都具备 下载 功能
  • 无法保证 pdf 预览 时,预览的字体和实际发票 字体 保持一致

现有大部分的预览方式都基于 pdf.js 的方式实现,而 pdf.js 内部通过 PDFJs.getDocument(url/buffer) 的方式基于 文件地址数据流 来获取内容,再通过 canvas 处理渲染 pdf 文件,感兴趣可以去研究 pdf.js 源码。

pdf.js 带来相关问题就是如果对应的 pdf 文件中包含了 pdf.js 中不存在的字体,那么就无法完整渲染,另外渲染出来的字体和原本的 pdf 文件字体会存在差异。

针对这两点,目前发现谷歌内置的 pdf 插件似乎提供了很好的支持,意味着其他浏览器如果包含了谷歌相关的插件(如:Edge、QQ Browser),就可以直接基于 <iframe> 的方式实现预览,又或者为了更严谨字体一致性只能通过下载的方式来查看源文件。

实现不了产品的要求怎么办?

例如上述探讨的方案其实无法满足文章开头提到的部分要求。产品提出需求的目的也是为了提供更好的用户体验(正常情况下),但是这些要求仍然要落实到技术上,而技术支持程度如何需要我们及时反馈(除非你的产品是技术经验),因此作为开发者你需要提供充足的内容向产品证明,然后自己再给出一些间接实现的方案(又或者产品自己就给出新的方案),看是否符合 第二预期,核心就是 合理沟通 + 其他方案每个人的处境不同,实际情况也许 ... 懂得都懂)。

以上是个人的一些看法和理解,有不当之处,可以在评论区指正!!!

希望本文对你有所帮助!!!



目录
相关文章
|
2月前
|
数据采集 Web App开发 JavaScript
Puppeteer自动化:使用JavaScript定制PDF下载
在现代Web开发中,自动化工具如Puppeteer可显著提升效率并减少重复工作。Puppeteer是一款强大的Node.js库,能够控制无头Chrome或Chromium浏览器,适用于网页快照生成、数据抓取及自动化测试等任务。本文通过示例展示了如何使用Puppeteer自动化生成定制化的PDF文件,并介绍了如何通过配置代理IP、设置user-agent和cookie等技术增强自动化过程的灵活性与稳定性。具体步骤包括安装Puppeteer、配置代理IP、设置user-agent和cookie等,最终生成符合需求的PDF文件。此技术可应用于报表生成、发票打印等多种场景。
153 6
Puppeteer自动化:使用JavaScript定制PDF下载
|
2月前
|
前端开发
PDF文件上传转成base64编码并支持预览
PDF文件上传转成base64编码并支持预览
157 12
|
5天前
|
JavaScript
jquery图片和pdf文件预览插件
EZView.js是一款jquery图片和pdf文件预览插件。EZView.js可以为图片和pdf格式文件生成在线预览效果。支持的文件格式有pdf、jpg、 png、jpeg、gif。
36 16
|
4月前
|
XML 缓存 JSON
为什么浏览器中有些图片、PDF等文件点击后有些是预览,有些是下载
为什么浏览器中有些图片、PDF等文件点击后有些是预览,有些是下载
286 0
|
1月前
|
前端开发 API
前端界面生成PDF并导出下载
【10月更文挑战第21天】利用合适的第三方库,你可以在前端轻松实现界面生成 PDF 并导出下载的功能,为用户提供更方便的文档分享和保存方式。你还可以根据具体的需求进一步优化和定制生成的 PDF 文件,以满足不同的业务场景要求。
|
4月前
|
Web App开发 iOS开发 容器
Vue3PDF预览(vue3-pdf-app)
`vue3-pdf-app` 插件提供了一个简单而强大的 PDF 预览解决方案。通过 `&lt;a&gt;` 标签即可快速预览 PDF 文件。为满足更复杂的定制需求,提供了 `PDFViewer.vue` 组件,基于 `vue3-pdf-app@1.0.3` 封装,支持多种功能如缩放、旋转、全屏预览、打印等,并可自定义主题颜色与语言。组件属性包括文件地址 (`src`)、预览容器尺寸 (`width`, `height`)、默认缩放规则 (`pageScale`) 和主题 (`theme`) 等。适用于多种浏览器,方便集成到项目中。
863 2
Vue3PDF预览(vue3-pdf-app)
|
3月前
|
移动开发 前端开发 JavaScript
使用html-to-image代替html2canvas,结合jspdf实现下载pdf(下载截图下载前端dom元素)
本文介绍了在前端项目中,当使用`html2canvas`遇到问题时,如何使用`html-to-image`库作为替代方案,结合`jspdf`实现将DOM元素生成为PDF文件并提供下载。文章首先讨论了`html2canvas`可能遇到的问题,并提供了该库的使用示例代码。随后,详细介绍了`html-to-image`库的安装和使用方法,展示了如何将DOM元素转换为Canvas,再利用`jspdf`生成PDF文件。最后,文章通过示例代码说明了整个转换和下载的过程,并展示了效果截图。
163 0
|
4月前
|
移动开发 资源调度 JavaScript
Vue移动端网页(H5)预览pdf文件(pdfh5和vue-pdf)
这篇文章介绍了在Vue移动端网页中使用`pdfh5`和`vue-pdf`两个插件来实现PDF文件的预览,包括滚动查看、缩放、添加水印、分页加载、跳转指定页数等功能。
3704 0
Vue移动端网页(H5)预览pdf文件(pdfh5和vue-pdf)
|
5月前
|
JavaScript 前端开发 程序员
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
JavaScript是Web标准语言,广泛应用于各类浏览器,造就了其最广泛部署的地位。Node.js的兴起扩展了JavaScript的使用场景,使其成为开发者首选语言。无论新手还是经验丰富的程序员,都能受益于学习JavaScript。[《JavaScript权威指南第7版》资源链接](https://zhangfeidezhu.com/?p=224)
334 5
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
|
2天前
|
人工智能 文字识别 数据挖掘
MarkItDown:微软开源的多格式转Markdown工具,支持将PDF、Word、图像和音频等文件转换为Markdown格式
MarkItDown 是微软开源的多功能文档转换工具,支持将 PDF、PPT、Word、Excel、图像、音频等多种格式的文件转换为 Markdown 格式,具备 OCR 文字识别、语音转文字和元数据提取等功能。
42 8
MarkItDown:微软开源的多格式转Markdown工具,支持将PDF、Word、图像和音频等文件转换为Markdown格式