需求
今天小程序同事开发说pdf预览有问题,手机端会先下载pdf然后在手机本地打开,但是有的手机解析不了pdf,也就会看不上页面,从而问pc上传的时候,能把pdf解析成图片么。
分析
1.上传前需要前端解析pdf为图片
2.解析的png图片需要上产阿里云
3.保证上传顺序
解决方案
根据调研,我们把问题1的解决方案为用pdfjs,因为我们是vue开发,所以用的二次封装组件pdfjs-dist。 第一步很简单创建组件 直接引入就好了
import PDFJS from 'pdfjs-dist'
要想获取本地文件,肯定要input了所以template部分代码,这里需要自己美化一下上传样式。本文章主题功能实现,不过多优化样式了。
<template>
<div>
<input id="file" type="file" name="file" accept="pdf" @change="loadPDF" />
</div>
</template>
然后就是点击上传,获取到上传文件,最文件进行blob处理。为什么要进行处理,应为pdf二次组件传参的时候你会发现他只能解析blob。
public file_name = '';
// 加载PDF文件
public async loadPDF(e: any) {
console.log(e.target.files[0]);
const file = e.target.files[0]
const reader = new FileReader(); // 阅读文件的方法,实例化下
this.file_name = e.target.files[0].name.split('.')[0]; // 获取文件名 后续用到
reader.onload = (e) => {
// 成功的回调
const url: string | null | ArrayBuffer = e.target ? e.target.result : '';
this.showPDF(url as string)
};
reader.readAsDataURL(file);
}
然后就进入了showPDF的方法里了,这里就是主要逻辑,话不多说 来分析下吧
public async showPDF(url: string) {
const pdf: any = await PDFJS.getDocument(url); // 这里就是上面说的,url参数如果是本地需要blob格式。
const pages = pdf.numPages; // 获取总条数
const imgArr = []; // 最后返回的上传后的图片集合
EventBus.$emit('warning', '上传中,请勿操作');
for (let i = 1; i <= pages; i++) {
// pdf转图片其实就是通过canvas转成png, html2canvas也是这个原理
const canvas = document.createElement('canvas')
const page = await pdf.getPage(i)
const viewport = page.getViewport(2)
const context = canvas.getContext('2d')
canvas.height = viewport.height
canvas.width = viewport.width
const renderContext = {
canvasContext: context,
viewport
}
await page.render(renderContext)
// 这里本来要直接上传的oss的。
// 但是上传到oss是不可以直接上传base64的,需要把base64转换成blob,然后把blob转换成file文件的形式
const image: any = canvas.toDataURL('image/png');
const _fileBlob = this.dataURLtoBlob(image);
const fileOfBlob = new File([_fileBlob], `${this.file_name}_page_${i}.png`);
// 直连阿里云oss 并保存返回值
imgArr.push(await this.upload({file: fileOfBlob}))
}
// 判断是否所有的都上传成功
if(imgArr.length === pages) {
EventBus.$emit('success', '上传成功');
this.$emit('on-success', imgArr)
} else {
EventBus.$emit('error', '上传失败');
}
}
base64 转blob方法
public dataURLtoBlob(dataurl: string) {
const arr = dataurl.split(',');
const reg = /:(.*?);/
const res = arr[0].match(reg);
const mime = res ? res[1] : '';
const bstr = atob(arr[1]);
let len = bstr.length;
const u8arr = new Uint8Array(len);
while (len--) {
u8arr[len] = bstr.charCodeAt(len);
}
return new Blob([u8arr], { type: mime });
}
最后直连oss
public async upload(option: any) {
const {file = {}} = option;
try {
const credentials = await getPcBasicOss({
bucket: xxx
});
return new Promise(async (res, rej) => {
const zipfile = new File([file], file.name, {type: 'image/png', lastModified: Date.now()});
const client = new OSS({
region: credentials.region,
accessKeyId: credentials.AccessKeyId, // 临时accessKeyId,跟申请的唯一key不同,防止泄露
accessKeySecret: credentials.AccessKeySecret , // 临时accessKeySecret,跟申请的唯一key不同,防止泄露
stsToken: credentials.SecurityToken,
bucket: credentials.bucket,
});
const date = new Date();
const d = dayjs(date).format('YYYY-MM-DD').replace(/\-/g, '');
let code = '';
for (let i = 1; i <= 6; i++) {
const num = Math.floor(Math.random() * 10);
code += num;
}
// 因为阿里云上传没有返回值 所以需要自己拼
const name = zipfile.name.substr(0, zipfile.name.lastIndexOf('.')) + '_' + d + code + zipfile.name.substr(zipfile.name.lastIndexOf('.'), zipfile.name.length);
const result = await client.put(`/${name}`, zipfile);
res(result);
})
} catch (e) {
console.log(e);
}
}
测试功能通过,可以 过个好年了,附上整个文件代码
<script lang="ts">
import {Vue, Component} from 'vue-property-decorator';
import PDFJS from 'pdfjs-dist'
import * as OSS from 'ali-oss';
import dayjs from 'dayjs';
import { getPcBasicOss } from '@/model';
import { EventBus } from 'feok-lib';
export interface DataType {
path: string; // 路径
name: string; // 名称
}
@Component
export default class PdfToImg extends Vue {
public file_name = '';
// 加载PDF文件
public async loadPDF(e: any) {
console.log(e.target.files[0]);
const file = e.target.files[0]
const reader = new FileReader();
this.file_name = e.target.files[0].name.split('.')[0];
reader.onload = (e) => {
const url: string | null | ArrayBuffer = e.target ? e.target.result : '';
this.showPDF(url as string)
};
reader.readAsDataURL(file);
}
public async showPDF(url: string) {
const pdf: any = await PDFJS.getDocument(url);
const pages = pdf.numPages;
const imgArr = [];
EventBus.$emit('warning', '上传中,请勿操作');
for (let i = 1; i <= pages; i++) {
const canvas = document.createElement('canvas')
const page = await pdf.getPage(i)
const viewport = page.getViewport(2)
const context = canvas.getContext('2d')
canvas.height = viewport.height
canvas.width = viewport.width
const renderContext = {
canvasContext: context,
viewport
}
await page.render(renderContext)
const image: any = canvas.toDataURL('image/png');
const _fileBlob = this.dataURLtoBlob(image);
const fileOfBlob = new File([_fileBlob], `${this.file_name}_page_${i}.png`);
imgArr.push(await this.upload({file: fileOfBlob}))
}
if(imgArr.length === pages) {
EventBus.$emit('success', '上传成功');
this.$emit('on-success', imgArr)
} else {
EventBus.$emit('error', '上传失败');
}
}
public dataURLtoBlob(dataurl: string) {
const arr = dataurl.split(',');
const reg = /:(.*?);/
const res = arr[0].match(reg);
const mime = res ? res[1] : '';
const bstr = atob(arr[1]);
let len = bstr.length;
const u8arr = new Uint8Array(len);
while (len--) {
u8arr[len] = bstr.charCodeAt(len);
}
return new Blob([u8arr], { type: mime });
}
public async upload(option: any) {
const {file = {}} = option;
try {
const credentials = await getPcBasicOss({
bucket: process.env.NODE_ENV === 'production' ? 'asal':'zxtest001'
});
return new Promise(async (res, rej) => {
const zipfile = new File([file], file.name, {type: 'image/png', lastModified: Date.now()});
const client = new OSS({
region: credentials.region,
accessKeyId: credentials.AccessKeyId,
accessKeySecret: credentials.AccessKeySecret ,
stsToken: credentials.SecurityToken,
bucket: credentials.bucket,
});
const date = new Date();
const d = dayjs(date).format('YYYY-MM-DD').replace(/\-/g, '');
let code = '';
for (let i = 1; i <= 6; i++) {
const num = Math.floor(Math.random() * 10);
code += num;
}
const name = zipfile.name.substr(0, zipfile.name.lastIndexOf('.')) + '_' + d + code + zipfile.name.substr(zipfile.name.lastIndexOf('.'), zipfile.name.length);
const result = await client.put(`/l-web-pc/${name}`, zipfile);
res(result);
})
} catch (e) {
console.log(e);
}
}
}
</script>
<template>
<div>
<input id="file" type="file" name="file" accept="pdf" @change="loadPDF" />
</div>
</template>
<style lang="less">
</style>
遇到的坑
之前一直提示PDFJS是undefined,各种查才发现,版本问题,最后只能吧版本锁定在"pdfjs-dist": "2.0.943"
参考
developer.aliyun.com/article/716…
如果这篇文章有用,欢迎评论, 点赞, 加关注。
我是leo:祝大家早日升职加薪,提前给大家拜个早年。