前端实现pdf下载与预览

804 阅读2分钟

一、pdf下载

1.1 需求描述

开发聚合支付系统时,遇到报表pdf文件下载(单个或多个)的需求
后端会返回pdf下载地址,若是仅一个pdf下载地址,最直接的方式就是跳转该地址,会到达一个pdf预览页面(里边包含下载操作)。但是,客户的需求是:
在当前页面提供下载按钮,分两种情况

  1. 一个pdf下载地址,直接导出
  2. 多个pdf地址,导出zip压缩包

查阅众多文章后,实现该需求,并封装成了函数。如下所示:

1.2 @/uitls/download-pdf.ts

import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { saveAs } from 'file-saver'
import JSZip from 'jszip'
import { Notification } from 'element-ui'


/**
 * 传入一个pdf下载地址,直接导出pdf
*/
type TDownloadPDFType = {
  PDFUrl: string,//pdf下载地址
  exportFileName: string,//导出pdf文件名称
  headers?: {[propName: string]: string}//请求头[可选]
}
export const downloadPDF = (
  params: TDownloadPDFType,
  startCallback?: Function,
  endCallback?: Function
) => {
  //==============axios方式==============
  // 开始回调函数
  if(startCallback) {
    startCallback();
  }

  const config: AxiosRequestConfig = {
    url: params.PDFUrl,
    method: 'GET',
    responseType: 'blob',
  }
  //是否有请求头
  if(params.headers) {
    config.headers = params.headers;
  }

  axios(config).then((res: AxiosResponse) => {
    saveAs(res.data, `${params.exportFileName}.pdf`);
    // 结束回调函数
    if(endCallback) {
      endCallback();
      Notification.success({
        title: '下载成功',
        message: '请根据需要修改文件名称'
      })
    }
  }).catch(() => {
    // 结束回调函数
    if(endCallback) {
      endCallback();
      Notification.error({
        title: '下载失败',
        message: '请检查网络或者联系管理员!'
      })
    }
  })

  //==============原生ajax方式==============
  // var oReq = new XMLHttpRequest();
  // // The Endpoint of your server
  // var URLToPDF = params.PDFUrl;

  // // Configure XMLHttpRequest
  // oReq.open("GET", URLToPDF, true);

  // // Important to use the blob response type
  // oReq.responseType = "blob";

  // // When the file request finishes
  // // Is up to you, the configuration for error events etc.
  // oReq.onload = function() {
  //     // Once the file is downloaded, open a new window with the PDF
  //     // Remember to allow the POP-UPS in your browser
  //     var file = new Blob([oReq.response], {
  //         type: 'application/pdf',
  //     });
    
  //     // Generate file download directly in the browser !
  //     saveAs(file, `${params.exportFileName}.pdf`);
  // };

  // oReq.send();
}


//源文件数组对象类型
export interface ISourceFileObj {
  fileUrl: string,//文件地址
  fileName: string//保存文件名
} 
/**
 * 打包压缩下载
 * @param sourceFileObjArr  源文件数组对象数组
 * @param zipName  压缩包的名称
*/
export const compressAndDownload = (
  sourceFileObjArr: ISourceFileObj[],
  zipName?: string,
  startCallback?: Function,
  endCallback?: Function
) => {
  // 开始回调函数
  if(startCallback) {
    startCallback();
  }

  const getFile = (url: string) => {
    return new Promise((resolve, reject) => {
      const config: AxiosRequestConfig = {
        url,
        method: 'GET',
        responseType: 'blob'
      }

      axios(config).then((res: AxiosResponse) => {
        resolve(res.data);
      }).catch((err: Error) => {
        reject(err)
      })
    })
  }

  const zip = new JSZip();
  const promises: any[] = [];  //用于存储多个promise
  sourceFileObjArr.forEach((item) => {
    const promise = getFile(item.fileUrl).then((res: any) => {
      const fileName = item.fileName
      zip.folder(zipName ? zipName : "pdf压缩包")?.file(fileName, res ,{binary: true});
    })
    promises.push(promise)
  })

  Promise.all(promises).then(() => {
    zip.generateAsync({
      type: "blob",
      compression: "DEFLATE",  // STORE:默认不压缩 DEFLATE:需要压缩
      compressionOptions: {
        level: 9               // 压缩等级1~9    1压缩速度最快,9最优压缩方式
      }
    }).then((res: any) => {
      saveAs(res, zipName ? zipName : "pdf压缩包") // 利用file-saver保存文件

      // 结束回调函数
      if(endCallback) {
        endCallback();
        Notification.success({
          title: '下载成功',
          message: '请根据需要修改文件名称'
        })
      }
    }).catch(() => {
      // 结束回调函数
      if(endCallback) {
        endCallback();
        Notification.error({
          title: '下载失败',
          message: '请检查网络或者联系管理员!'
        })
      }
    })
  })
}

二、pdf预览

单个pdf预览可以通过直接跳转实现,但是,客户的需求是:
在自己系统的页面,预览 单个或多个 pdf
下面记录了实现技术点与思路:

/**技术点
// 1.pdf预览标签(currentPdfUrl变量是pdf地址)
<object :data="currentPdfUrl" type="application/pdf" width="100%" height="100%">
  <embed :src="currentPdfUrl">
    <p>
      This browser does not support PDFs. Please download the PDF to view it: 
      <a :href="currentPdfUrl">Download PDF</a>
    </p>
</object>

// 2.路由跳转打开新页面(vue-router路由跳转不会打开新页面,这里预览还是建议使用下面代码,跳转新页面预览)
const newThing = this.$router.resolve({path: '/single-report-preview'});
window.open(newThing.href, '_blank');
*/

/**思路
从 技术点1 已知能通过标签预览pdf,只需在自己的页面使用该标签,就已经实现pdf预览了
对于多个pdf预览,可在预览页面加上单选框,每次点击一个新选项,则切换为新的pdf地址
*/