截图 && 转换PDF && 并压缩下载

535 阅读1分钟

前言

最近遇到个需求,需要前端将页面以PDF的形式下载下来,并且支持批量下载,所以实现后,在此做个记录,希望可以帮助到更多的同学;

前置依赖

$ yarn add html2canvas jspdf jszip file-saver

截图

截图选用 html2canvas
文件名screenshots.js

import html2canvas from 'html2canvas';
import { fileToPDF } from './fileUtils';

/* eslint-disable no-undef */
/**
 * [saveImg description]
 *
 * @param   {[dom|object]}  el  [el description]
 * @param   {[params]}  params  name: 文件名字, type, fileType
 *
 * @return  {[Promise]}      [return Promise]
 */
function saveImg(el, { name, type, fileType }) {
    return new Promise((resolve, reject) => {

        let dom = el;
        if (!el.nodeName) {
            dom = el.el;
        }

        // 获取元素样式
        const box = window.getComputedStyle(dom);
        // DOM 节点计算后宽高
        const width = parseValue(box.width);
        const height = parseValue(box.height);

        // 获取像素比
        const scaleBy = DPR();
        // 创建自定义 canvas 元素,设置高清图片
        const canvas = document.createElement('canvas');
        canvas.width = width * scaleBy;
        canvas.height = height * scaleBy;
        // 设定 canvas css宽高为 DOM 节点宽高
        canvas.style.width = `${width}px`;
        canvas.style.height = `${height}px`;

        // 获取画笔
        const context = canvas.getContext('2d');
        context.scale(scaleBy, scaleBy);

        html2canvas(dom, {
            canvas,
            backgroundColor: 'transparent',
            useCORS: true
        }).then(function(canvas) {
            // 获取图片的base64
            if (type !== 'pdf') {
                const toDataURL = canvas.toDataURL(type, 1.0);
                resolve(toDataURL);
            } else {
                const pageData = canvas.toDataURL(type || 'image/png', 1.0);
                // console.log(pageData);
                const obj = {
                    pageData,
                    canvas,
                    name,
                    type: fileType || 'base64'
                };
                resolve(fileToPDF(obj));
            }
        });
    });
}
// 四舍五入及计算数值
function parseValue(value) {
    return parseInt(value, 10);
}
// 获取像素比
function DPR() {
    if (window.devicePixelRatio && window.devicePixelRatio > 1) {
        return window.devicePixelRatio;
    }
    return 1;
}

export default saveImg;

图片转换成PDF

文件名 fileUtils.js

import JsPDF from 'jspdf';

/**
 *
 * @param {*} pageData 页面数据
 * @param {*} canvas canvas 元素
 * @param {*} name 文件名
 * @param {*} type 文件类型
 * @returns
 */
export const fileToPDF = ({ pageData, canvas, name, type }) => {
    var contentWidth = canvas.width;
    var contentHeight = canvas.height;
    // 一页pdf显示html页面生成的canvas高度;
    var pageHeight = contentWidth / 592.28 * 841.89;
    // 未生成pdf的html页面高度
    var leftHeight = contentHeight;
    // 页面偏移
    var position = 0;
    // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
    var imgWidth = 595.28;
    var imgHeight = 595.28 / contentWidth * contentHeight;
    const pdf = new JsPDF('', 'pt', 'a4');

    // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
    // 当内容未超过pdf一页显示的范围,无需分页
    if (leftHeight < pageHeight) {
        // 在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;
        pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
        // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
    } else { // 分页
        while (leftHeight > 0) {
            pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
            leftHeight -= pageHeight;
            position -= 841.89;
            // 避免添加空白页
            if (leftHeight > 0) {
                pdf.addPage();
            }
        }
    }

    const pdffile = pdf.output('datauristring');

    if (type && type === 'base64') {
        return pdffile;
    }
    const blob = dataURLtoFile(pdffile, name);

    return blob;
};

// 文件转换成blob类型
function dataURLtoFile(dataurl, filename) {
    var arr = dataurl.split(',');
    var mime = arr[0].match(/:(.*?);/)[1];
    var bstr = atob(arr[1]);
    var n = bstr.length;
    var u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, { type: mime });
}

文件压缩下载与批量下载

文件名 fileUtils.js

import JSZip from 'jszip';
import FileSaver from 'file-saver';

/**
 *
 * @param {*} fileName 文件名字,支持数组
 * @param {*} content 文件内容,支持数组
 * @param {*} downName 下载文件夹名称
 * @param {*} fileType 文件传输类型
 */
export const download = (fileName, content, downName, fileType) => {
    console.log('---- 开始压缩下载 ----');
    const zip = new JSZip();
    const imgTypes = ['gif', 'jpg', 'jpeg', 'png', 'pdf'];

    const setZipFile = (name, _content) => {
        const type = name.split('.').pop();
        if (imgTypes.includes(type) || fileType === 'base64') {
            zip.file(name, _content, { base64: true });
        } else {
            zip.file(name, _content);
        }
    };

    // 如果是数组需要依次生成
    if (Object.prototype.toString.call(fileName) === '[object Array]') {
        fileName.forEach((value, key) => {
            setZipFile(value, content[key]);
        });
    } else {
        setZipFile(fileName, content);
    }

    // 保存生成压缩包
    zip.generateAsync({ type: 'blob' })
        .then((content) => {
            const time = new Date().getTime();
            FileSaver(content, `${downName ? downName + '-' + time : time}.zip`);
        });
};

综合使用

import saveImg from './screenshots';
import { download } from './fileUtils';

const el = document.querySelector('#app')
const img1 = await saveImg(el, { type: 'pdf', name: 'ceshi.pdf', fileType: 'blob' });
const img2 = await saveImg(el, { type: 'pdf', name: 'ceshi2.pdf', fileType: 'blob' });
 
// 多数据转换下载,需要内容与名称一一对应
const pdfArr = [img1, img2];
const pdfNames = ['ceshi1.pdf', 'ceshi2.pdf'];
download(pdfNames, pdfArr, '压缩下载', 'base64');
 
 
// 单个数据下载
download('ceshi1.pdf', img1, '压缩下载', 'base64');

以上就是本次的内容了,希望可以帮助到大家