- A+
一、实现效果
选择本地 pdf 文件上传,会生成 pdf 文件的预览,点击保存功能。
二、所用插件
- pdf文件相关的文件处理插件:
pdfjs-dist@2.3.200
- zip压缩文件相关的包:
jszip@3.5.0
- 文件保存下载相关的包:
file-saver@2.0.2
三、相关代码
vue 中测试
<!-- * @Date: 2020-10-21 10:44:54 * @information: pdf 转图片并下载 --> <template> <div id="page09"> <div class="info-box"> <div class="input"> <input id="input" type="file" accept="application/pdf" @change="convertFile()"/> </div> <div class="cell"> <div>名称:{{fileName || '-'}}</div> <div>大小:{{Number(fileSize).toFixed(2)}}M</div> <div>页数:{{filePage}}</div> <button @click="onExportImg">保存图片</button> </div> </div> <div id="container"></div> </div> </template> <script> import PDFJS from 'pdfjs-dist' import JSZIP from 'jszip' import FileSaver from 'file-saver' export default { data() { return { // pdf地址 pdfPath2: `https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf`, // arrayBuffer arrayBuffer: null, // 文件名称 fileName: null, // 文件大小 fileSize: 0, // 文件页数 filePage: 0, } }, methods: { /** * 读取文件内容 */ convertFile() { let file = document.getElementById('input').files if(!file.length) return; let {name, size} = file[0] Object.assign(this, {fileName: name, fileSize: size/1024/1024}) // 使用FileReader对象,web应用程序可以异步的读取存储在用户计算机上的文件(或者原始数据缓冲)内容,可以使用File对象或者Blob对象来指定所要处理的文件或数据 let fileReader = new FileReader() // 异步按字节读取文件内容,结果用ArrayBuffer对象表示 fileReader.readAsArrayBuffer(file[0]) fileReader.onload = (e) => { let arrayBuffer = this.arrayBuffer = e.target.result // 创建canvas节点 this.createCanvas(arrayBuffer) } }, /** * 创建canvas */ createCanvas(val) { // 清空节点下数据 document.getElementById('container').innerHTML = '' // 使用getTextContent获取pdf内容 PDFJS.getDocument(val).promise.then(el => { let filePage = this.filePage = el.numPages for(let i = 1; i <= filePage; i ++) { let canvas = document.createElement('canvas') canvas.id = `pageNum-${i}` let context = canvas.getContext('2d') document.getElementById('container').append(canvas) // 渲染canvas this.openPage(el, i, context) } }) }, /** * 渲染canvas */ openPage(pdfFile, pageNumber, context) { // 获取PDF文档中的各个页面 pdfFile.getPage(pageNumber).then(page => { // 设置展示比例 let scale = 3 // 获取pdf尺寸 let viewport = page.getViewport(scale) let canvas = context.canvas canvas.width = viewport.width canvas.height = viewport.height canvas.style.width = "100%" canvas.style.height = "100%" let model = { canvasContext: context, viewport: viewport, } // 渲染PDF page.render(model) }) }, /** * 保存图片 */ onExportImg() { if(!this.arrayBuffer) { alert(`请上传pdf文件`) return; } let jszip = new JSZIP() // 解压缩后的文件夹名称 let images = jszip.folder("images") let eleList = document.querySelectorAll('canvas') // 遍历所有canvas节点 for(let i = 0; i < eleList.length; i ++) { let canvas = document.getElementById(`pageNum-${i+1}`) // 向此文件夹中加入文件 // toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi images.file(`image-${i+1}.png`, this.dataURLtoBlob(canvas.toDataURL("image/png", 1.0)), { base64: true }) } // 生成一个zip文件 jszip.generateAsync({ type: "blob" }).then((content) => { // 使用 FileSaver 保存下载 zip 文件 FileSaver.saveAs(content, "pdfToImages.zip"); }) }, /** * dataURL 转成 Blob */ dataURLtoBlob(val) { let arr = val.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n --) { // charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数 u8arr[n] = bstr.charCodeAt(n) } // new Blob(blobParts, [options]) 参数说明: // 1. blobParts:数组类型,数组中的每一项连接起来构成Blob对象的数据,数组中的每项元素可以是ArrayBuffer, ArrayBufferView, Blob, DOMString // 2. options:可选项,字典格式类型,可以指定如下两个属性: // (1) type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。 // (2) endings,默认值为"transparent",用于指定包含行结束符n的字符串如何被写入。 它是以下两个值中的一个: "native",表示行结束符会被更改为适合宿主操作系统文件系统的换行符; "transparent",表示会保持blob中保存的结束符不变 return new Blob([u8arr], { type: mime }) }, }, created() { }, mounted() { } } </script> <style lang="scss"> #page09 { width: 1000px; margin: 0 auto; .info-box { position: relative; .input { margin: 15px 0; #input { width: 100%; height: 100%; cursor: pointer; } } .cell { margin: 15px 0; display: flex; justify-content: space-around; div { margin-right: 20px; } } } #container { width: 100%; min-height: 850px; margin: 0 auto; border: 1px solid #eee; border-radius: 10px; canvas { margin-bottom: 10px; border: 1px solid #ff6700; border-radius: 10px; } } } </style>
四、相关知识点
1. 使用 FileReader 进行文件读取
(1)创建实例
let fileReader = new FileReader()
(2)方法
方法定义 | 描述 |
---|---|
abort():void | 终止文件读取操作 |
readAsArrayBuffer(file):void | 异步按字节读取文件内容,结果用ArrayBuffer对象表示 |
readAsBinaryString(file):void | 异步按字节读取文件内容,结果为文件的二进制串 |
readAsDataURL(file):void | 异步读取文件内容,结果用data:url的字符串形式表示 |
readAsText(file,encoding):void | 异步按字符读取文件内容,结果用字符串形式表示 |
(3)事件: FileReader 通过异步的方式读取文件内容,结果均是通过事件回调获取。
方法定义 | 描述 |
---|---|
onabort | 当读取操作被中止时调用 |
onerror | 当读取操作发生错误时调用 |
onload | 当读取操作成功完成时调用 |
onloadend | 当读取操作完成时调用,不管是成功还是失败 |
onloadstart | 当读取操作将要开始之前调用 |
onprogress | 在读取数据过程中周期性调用 |
2. 使用 canvas 的 toDataURL()
方法返回 包含 data URI 的DOMString
let canvas = document.getElementById(`canvas`) // 参数说明: // 1. type(可选):图片格式,默认为 image/png // 2. encoderOptions(可选):在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。 let result = canvas.toDataURL("image/png", 1.0)
3. ArrayBuffer 二进制数组
(1) JS 中的二进制数据格式,例:ArrayBuffer,Uint8Array,DataView,Blob,File 及其他。
基本的二进制对象是 ArrayBuffer —— 对固定长度的连续内存空间的引用。
(2)如要操作 ArrayBuffer,我们需要使用“视图”对象。 视图对象本身并不存储任何东西。它是一副“眼镜”,透过它来解释存储在 ArrayBuffer 中的字节。例如下:
视图类型 | 说明 |
---|---|
Uint8Array | 将 ArrayBuffer 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。 |
Uint16Array | 将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。 |
Uint32Array | 将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。 |
Float64Array | 将每 8 个字节视为一个 5.0x10-324 到 1.8x10308 之间的浮点数。 |
3. Blob
(1)概述: Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
(2)创建 Blob 对象
// 参数说明 // 1. blobParts: 数组类型,数组中的每一项连接起来构成Blob对象的数据,数组中的每项元素可以是ArrayBuffer, ArrayBufferView, Blob, DOMString // 2. options:可选项,字典格式类型,可以指定如下两个属性: type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。 endings,默认值为"transparent",用于指定包含行结束符n的字符串如何被写入。 它是以下两个值中的一个: "native",表示行结束符会被更改为适合宿主操作系统文件系统的换行符; "transparent",表示会保持blob中保存的结束符不变。 let blob = new Blob(blobParts[, options]) let data1 = "a" let data2 = { "name": "abc" } let blob1 = new Blob([data1]) let blob2 = new Blob([JSON.stringify(data4)]) let blob3 = new Blob([data4]) console.log(blob1); // Blob {size: 1, type: ""} console.log(blob2); // Blob {size: 14, type: ""} console.log(blob3); // Blob {size: 15, type: ""}
size代表Blob 对象中所包含数据的字节数。这里要注意,使用字符串和普通对象创建Blob时的不同,blob4使用通过JSON.stringify把data4对象转换成json字符串,blob5则直接使用data4创建,两个对象的size分别为14和15。blob4的size等于14很容易理解,因为JSON.stringify(data4)的结果为:"{"name":"abc"}",正好14个字节(不包含最外层的引号)。blob5的size等于15是如何计算而来的呢?实际上,当使用普通对象创建Blob对象时,相当于调用了普通对象的toString()方法得到字符串数据,然后再创建Blob对象。所以,blob5保存的数据是"[object Object]",是15个字节(不包含最外层的引号)。
(3)Blob 方法: slice()
(4)Blob使用场景
- 分片上传
- Blob URL
参考:PDF转成图片