前端导出大量数据到PDF方案

前端导出大量数据到PDF的方案:

1. 纯前端方案

jsPDF + autoTable(最常用)

import jsPDF from 'jspdf';
import 'jspdf-autotable';

function exportToPDF(data) {
  const doc = new jsPDF();
  
  // 设置表格列
  const columns = ['ID', '姓名', '邮箱', '部门'];
  
  // 转换数据
  const rows = data.map(item => [
    item.id,
    item.name,
    item.email,
    item.department
  ]);
  
  // 生成表格
  doc.autoTable({
    head: [columns],
    body: rows,
    styles: { fontSize: 8 },
    margin: { top: 10 }
  });
  
  doc.save('数据导出.pdf');
}

分页处理大量数据

function exportLargeDataToPDF(data, chunkSize = 500) {
  const doc = new jsPDF();
  const columns = ['ID', '姓名', '邮箱'];
  
  for (let i = 0; i < data.length; i += chunkSize) {
    if (i !== 0) {
      doc.addPage(); // 添加新页
    }
    
    const chunk = data.slice(i, i + chunkSize);
    const rows = chunk.map(item => [item.id, item.name, item.email]);
    
    doc.autoTable({
      head: [columns],
      body: rows,
      startY: 20,
      styles: { fontSize: 7 },
      pageBreak: 'auto'
    });
  }
  
  doc.save('大数据导出.pdf');
}

2. 性能优化方案

虚拟滚动 + 分批处理

async function exportHugeData(data, batchSize = 1000) {
  const doc = new jsPDF();
  let currentPage = 1;
  
  for (let i = 0; i < data.length; i += batchSize) {
    const batch = data.slice(i, i + batchSize);
    
    // 显示进度
    updateProgress(i, data.length);
    
    if (i > 0) {
      doc.addPage();
      currentPage++;
    }
    
    // 使用Web Worker处理数据转换
    const rows = await processBatchInWorker(batch);
    
    doc.autoTable({
      head: [['ID', '姓名', '邮箱']],
      body: rows,
      startY: 20,
      styles: { fontSize: 6 },
      margin: { left: 5, right: 5 }
    });
    
    // 让出主线程避免阻塞
    await new Promise(resolve => setTimeout(resolve, 0));
  }
  
  doc.save('超大数据导出.pdf');
}

3. 服务端辅助方案

前端生成 + 服务端合并

// 前端:分批生成PDF片段
async function generatePDFChunks(data, chunkSize = 2000) {
  const chunks = [];
  
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    const pdfBlob = await generateChunkPDF(chunk);
    chunks.push(pdfBlob);
  }
  
  // 发送到服务端合并
  const finalPDF = await mergePDFsOnServer(chunks);
  downloadBlob(finalPDF, '合并报表.pdf');
}

4. 基于Canvas的方案

html2canvas + jsPDF

import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

async function exportDivToPDF(elementId, filename = '导出.pdf') {
  const element = document.getElementById(elementId);
  const canvas = await html2canvas(element, {
    scale: 2,
    useCORS: true,
    logging: false
  });
  
  const imgData = canvas.toDataURL('image/png');
  const pdf = new jsPDF({
    orientation: 'portrait',
    unit: 'mm',
    format: 'a4'
  });
  
  const imgProps = pdf.getImageProperties(imgData);
  const pdfWidth = pdf.internal.pageSize.getWidth();
  const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
  
  pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
  pdf.save(filename);
}

5. 流式处理方案

使用PDFKit(Node.js环境)

// 适用于Electron或Node.js环境
const PDFDocument = require('pdfkit');
const fs = require('fs');

function createStreamingPDF(data, outputPath) {
  const doc = new PDFDocument({ autoFirstPage: false });
  doc.pipe(fs.createWriteStream(outputPath));
  
  let rowCount = 0;
  const rowsPerPage = 40;
  
  data.forEach((item, index) => {
    if (rowCount % rowsPerPage === 0) {
      if (index > 0) doc.addPage();
      addTableHeader(doc);
    }
    
    addTableRow(doc, item, rowCount % rowsPerPage);
    rowCount++;
  });
  
  doc.end();
}

6. 最佳实践建议

内存管理

function optimizedExport(data) {
  // 1. 数据分片
  const chunks = chunkArray(data, 1000);
  
  // 2. 清理不需要的引用
  let currentChunkIndex = 0;
  
  function processNextChunk() {
    if (currentChunkIndex >= chunks.length) return;
    
    const chunk = chunks[currentChunkIndex];
    processChunk(chunk);
    
    // 释放内存
    chunks[currentChunkIndex] = null;
    currentChunkIndex++;
    
    // 非阻塞处理
    setTimeout(processNextChunk, 100);
  }
  
  processNextChunk();
}

进度反馈

function exportWithProgress(data) {
  const total = data.length;
  let processed = 0;
  
  showProgressBar();
  
  const interval = setInterval(() => {
    updateProgressBar((processed / total) * 100);
    
    if (processed >= total) {
      clearInterval(interval);
      showCompletionMessage();
    }
  }, 100);
}

7. 推荐方案选择

  • 中小数据量(< 10,000行):jsPDF + autoTable
  • 大数据量(10,000-100,000行):分页处理 + 进度反馈
  • 超大数据量(> 100,000行):服务端生成或流式处理
  • 复杂排版需求:html2canvas + jsPDF
  • 最高性能要求:Web Worker + 分批处理

实际项目中,建议根据数据量大小和性能要求选择合适的方案,并始终提供进度反馈以改善用户体验。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值