pdf转图片(使用低版本的pdf-dist,兼容vue2.6.12)

438 阅读3分钟

版本

vue : 2.6.12

pdf-dist: ^2.5.207

node : 14.18.2

背景

公司项目node版本较低, pdf-dist由于新版本的语法较新,项目不支持, 故选择一个低版本的pdf-dist

不同版本的pdf-dist的引入路径可能不同, 注意分辨

低版本的如下 :

import * as pdfjsLib from 'pdfjs-dist';
import * as PdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';

注意PdfjsWorker一定要引入, 即使没有显式的引用

步骤

pdf 转图片的 步骤如下, 这里我把pdf全都绘制成了一张图

  1. 获取文件对象
  2. 转换为arrayBuffer, 使用pdf-dist加载
  3. pdf-dist开始渲染, 并且用canvas绘制
  4. 拿到canvas的base64数据转换返回file对象 (视自己业务而定)

file转arrayBuffer

const fileToArrayBuffer = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = (error) => {
      reject(error);
    };
    reader.readAsArrayBuffer(file);
  });
};

base64转换为file对象

function base64ToFile(base64Data, filename) {
  // 分割base64的元数据和二进制数据
  const arr = base64Data.split(',');
  // 提取文件类型 例如 : image/jpeg
  const mime = arr[0].match(/:(.*?);/)[1];
  // 将base64编码格式解码为原始的二进制
  const bstr = atob(arr[1]);
  //用来存储解码后的二进制数据
  let n = bstr.length;
  const u8arr = new Uint8Array(n);

  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  // new File(bits, name, options) // bits: `ArrayBuffer`、`Blob`、`Uint8Array`
  return new File([u8arr], filename, { type: mime });
}

前提知识点提要

pdf-dist 官方示例 : mozilla.github.io/pdf.js/exam…

pdf-dist api文档 : mozilla.github.io/pdf.js/api/…

pdf-dist api:

  • 获取文档实例 : await pdfjsLib.getDocument({ data: arrayBuffer }).promise;

  • getPage(index) 获取pdf的指定页

  • canvas.getContext('2d'); 用于绘制canvas2d图像

  • page.render() 接受一个对象, 包含 canvas和pdf的viewport

  • drawImage: canvas,横坐标, 纵坐标

  • toDataURL: canvas的方法

代码实现

import * as pdfjsLib from 'pdfjs-dist';
import * as PdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
import system from '@/api/modules/system'; // 导入你定义的 API 对象

// 辅助函数:将 File 对象转换为 ArrayBuffer
const fileToArrayBuffer = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      console.log("File successfully read into ArrayBuffer:", reader.result);
      resolve(reader.result);
    };
    reader.onerror = (error) => {
      console.error("Error reading file:", error);
      reject(error);
    };
    reader.readAsArrayBuffer(file);
  });
};

const pdfConvertImg = async function(file) {
  try {
    // 将 File 转换为 ArrayBuffer
    const arrayBuffer = await fileToArrayBuffer(file);

    // 调试信息:确保 arrayBuffer 是有效的
    if (!arrayBuffer || arrayBuffer.byteLength === 0) {
      throw new Error("Invalid ArrayBuffer: file may be empty or corrupted.");
    }

    // 从 ArrayBuffer 加载 PDF
    const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;

    const canvases = [];
    let totalHeight = 0;
    let maxWidth = 0;

    // 渲染每一页并计算总高度和最大宽度
    for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
      const page = await pdf.getPage(pageNum);
      const viewport = page.getViewport({ scale: 1.5 });

      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');
      canvas.height = viewport.height;
      canvas.width = viewport.width;

      const renderContext = {
        canvasContext: context,
        viewport: viewport,
      };

      await page.render(renderContext).promise;

      canvases.push(canvas);
      totalHeight += viewport.height;
      maxWidth = Math.max(maxWidth, viewport.width);
    }

    // 创建一个新的 canvas 来合并所有页面
    const finalCanvas = document.createElement('canvas');
    finalCanvas.width = maxWidth;
    finalCanvas.height = totalHeight;
    const finalContext = finalCanvas.getContext('2d');

    let currentHeight = 0;
    for (let canvas of canvases) {
      finalContext.drawImage(canvas, 0, currentHeight);
      currentHeight += canvas.height;
    }

    // 将合并后的 canvas 转换为 Base64 图片
    const base64Image = finalCanvas.toDataURL('image/png');

    // 将 Base64 图片转换为 File 对象
    const fileResult = base64ToFile(base64Image, 'mergedImage.png');

    // 创建 FormData 并将文件对象附加到请求中
    const formData = new FormData();
    formData.append('file', fileResult);
    formData.append('businessType', '0');

    // 上传文件并返回结果 URL,使用导入的 API 对象进行文件上传
    return await system.file.uploadFile(formData);

  } catch (error) {
    console.error('加载或合并 PDF 时出错:', error);
    throw error;
  }
};

// 将 Base64 数据转换为 File 对象
function base64ToFile(base64Data, filename) {
  const arr = base64Data.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);

  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }

  return new File([u8arr], filename, { type: mime });
}

export default pdfConvertImg;