React-PDF与CockroachDB集成:从CockroachDB生成PDF

【免费下载链接】react-pdf 📄 Create PDF files using React 【免费下载链接】react-pdf 项目地址: https://gitcode.com/gh_mirrors/re/react-pdf

在现代Web应用开发中,将数据库数据转化为PDF文档是常见需求。本文将介绍如何结合React-PDF和CockroachDB,实现从CockroachDB数据库提取数据并生成专业PDF文档的完整流程。

技术栈概述

React-PDF是一个基于React的PDF生成库,允许开发者使用熟悉的JSX语法创建PDF文档。CockroachDB则是一个分布式SQL数据库,提供强一致性和高可用性。两者结合可构建可靠的PDF生成系统。

React-PDF核心组件包括:

  • <Document>:PDF文档容器
  • <Page>:页面组件
  • 基础排版组件:<Text><View><Image>
  • 样式系统:StyleSheet

CockroachDB提供:

  • 分布式SQL能力
  • 强一致性事务
  • 与PostgreSQL兼容的API

集成架构设计

以下是React-PDF与CockroachDB集成的基本架构:

mermaid

集成流程主要包含三个步骤:

  1. 从CockroachDB查询数据
  2. 使用React-PDF创建PDF模板
  3. 渲染并输出PDF文档

环境准备

首先确保安装必要的依赖:

# 安装React-PDF核心包
npm install @react-pdf/renderer

# 安装CockroachDB客户端
npm install pg

项目结构建议:

src/
├── db/
│   └── cockroach.js    # 数据库连接配置
├── pdf/
│   ├── templates/      # PDF模板组件
│   └── generator.js    # PDF生成逻辑
└── server.js           # 后端服务入口

数据库连接配置

创建CockroachDB连接配置文件db/cockroach.js:

const { Pool } = require('pg');

// CockroachDB连接配置
const pool = new Pool({
  user: 'your-user',
  host: 'your-cockroachdb-host',
  database: 'your-db',
  password: 'your-password',
  port: 26257,
  ssl: {
    rejectUnauthorized: false // 生产环境需配置CA证书
  }
});

// 测试连接
pool.query('SELECT NOW()', (err, res) => {
  if (err) {
    console.error('CockroachDB连接失败:', err.stack);
  } else {
    console.log('CockroachDB连接成功:', res.rows[0].now);
  }
});

module.exports = pool;

创建PDF模板

使用React-PDF创建一个基本的PDF模板pdf/templates/invoice.js:

import React from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';

// 创建样式表
const styles = StyleSheet.create({
  page: {
    padding: 30,
  },
  title: {
    fontSize: 24,
    marginBottom: 20,
    textAlign: 'center',
  },
  section: {
    marginBottom: 15,
  },
  table: {
    display: 'table',
    width: '100%',
    borderStyle: 'solid',
    borderWidth: 1,
    borderRightWidth: 0,
    borderBottomWidth: 0,
  },
  tableRow: {
    flexDirection: 'row',
  },
  tableCell: {
    width: '25%',
    borderStyle: 'solid',
    borderWidth: 1,
    borderLeftWidth: 0,
    borderTopWidth: 0,
    padding: 5,
  },
});

// PDF文档组件
const InvoiceTemplate = ({ data }) => (
  <Document>
    <Page size="A4" style={styles.page}>
      <Text style={styles.title}>发票 #{data.invoiceNumber}</Text>
      
      <View style={styles.section}>
        <Text>客户: {data.customer.name}</Text>
        <Text>日期: {new Date(data.date).toLocaleDateString()}</Text>
      </View>
      
      <View style={styles.table}>
        <View style={styles.tableRow}>
          <Text style={styles.tableCell}>产品</Text>
          <Text style={styles.tableCell}>数量</Text>
          <Text style={styles.tableCell}>单价</Text>
          <Text style={styles.tableCell}>金额</Text>
        </View>
        {data.items.map((item, index) => (
          <View style={styles.tableRow} key={index}>
            <Text style={styles.tableCell}>{item.name}</Text>
            <Text style={styles.tableCell}>{item.quantity}</Text>
            <Text style={styles.tableCell}>${item.price.toFixed(2)}</Text>
            <Text style={styles.tableCell}>${(item.quantity * item.price).toFixed(2)}</Text>
          </View>
        ))}
      </View>
      
      <View style={styles.section}>
        <Text>总计: ${data.total.toFixed(2)}</Text>
      </View>
    </Page>
  </Document>
);

export default InvoiceTemplate;

实现PDF生成服务

创建PDF生成服务pdf/generator.js:

const { renderToFile } = require('@react-pdf/renderer');
const fs = require('fs');
const path = require('path');
const pool = require('../db/cockroach');
const InvoiceTemplate = require('./templates/invoice');

// 从CockroachDB获取发票数据
async function getInvoiceData(invoiceId) {
  const query = `
    SELECT i.*, c.name as customer_name, c.email as customer_email,
           json_agg(json_build_object(
             'name', p.name,
             'quantity', ii.quantity,
             'price', ii.price
           )) as items,
           SUM(ii.quantity * ii.price) as total
    FROM invoices i
    JOIN customers c ON i.customer_id = c.id
    JOIN invoice_items ii ON i.id = ii.invoice_id
    JOIN products p ON ii.product_id = p.id
    WHERE i.id = $1
    GROUP BY i.id, c.name, c.email
  `;
  
  const result = await pool.query(query, [invoiceId]);
  return result.rows[0];
}

// 生成PDF发票
async function generateInvoicePDF(invoiceId, outputPath) {
  try {
    // 1. 从CockroachDB获取数据
    const invoiceData = await getInvoiceData(invoiceId);
    
    if (!invoiceData) {
      throw new Error(`未找到ID为${invoiceId}的发票`);
    }
    
    // 2. 格式化数据
    const formattedData = {
      invoiceNumber: invoiceData.invoice_number,
      date: invoiceData.invoice_date,
      customer: {
        name: invoiceData.customer_name,
        email: invoiceData.customer_email
      },
      items: invoiceData.items,
      total: parseFloat(invoiceData.total)
    };
    
    // 3. 使用React-PDF渲染PDF
    await renderToFile(
      <InvoiceTemplate data={formattedData} />,
      outputPath
    );
    
    console.log(`PDF生成成功: ${outputPath}`);
    return outputPath;
  } catch (error) {
    console.error('PDF生成失败:', error);
    throw error;
  }
}

module.exports = { generateInvoicePDF };

创建API接口

在server.js中创建API接口:

const express = require('express');
const fs = require('fs');
const path = require('path');
const { generateInvoicePDF } = require('./pdf/generator');

const app = express();
const PORT = process.env.PORT || 3000;
const PDF_OUTPUT_DIR = path.join(__dirname, '..', 'pdfs');

// 确保PDF输出目录存在
if (!fs.existsSync(PDF_OUTPUT_DIR)) {
  fs.mkdirSync(PDF_OUTPUT_DIR);
}

// 生成发票PDF的API接口
app.get('/api/invoices/:id/pdf', async (req, res) => {
  try {
    const invoiceId = req.params.id;
    const outputPath = path.join(PDF_OUTPUT_DIR, `invoice-${invoiceId}.pdf`);
    
    // 生成PDF
    await generateInvoicePDF(invoiceId, outputPath);
    
    // 返回PDF文件
    res.download(outputPath, `invoice-${invoiceId}.pdf`, (err) => {
      if (err) {
        console.error('文件下载错误:', err);
      }
      
      // 可选:删除临时文件
      // fs.unlinkSync(outputPath);
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

性能优化策略

对于大规模PDF生成,考虑以下优化策略:

  1. 数据库查询优化

    • 为常用查询创建适当索引
    • 使用LIMIT分页处理大量数据
    • 考虑使用CockroachDB的分布式查询优化
  2. PDF渲染优化

    • 使用流式渲染(renderToStream)处理大型文档
    • 避免在PDF中使用过多高分辨率图片
    • 考虑使用缓存机制存储重复使用的PDF片段
  3. 并发处理

    • 使用任务队列(如Bull)管理PDF生成任务
    • 限制同时渲染的PDF数量,避免资源耗尽

部署与扩展

在生产环境部署时需考虑:

  1. CockroachDB集群配置

    • 确保适当的副本数量
    • 配置自动负载均衡
    • 设置数据备份策略
  2. 应用扩展

    • 考虑使用容器化部署(Docker + Kubernetes)
    • 实现无状态服务设计,便于水平扩展
    • 使用共享存储或对象存储保存生成的PDF
  3. 监控与日志

    • 监控数据库连接池状态
    • 记录PDF生成性能指标
    • 设置错误报警机制

完整示例与演示

以下是使用Postman测试API的示例截图:

API测试截图

成功生成的PDF文档示例:

PDF示例截图

总结与最佳实践

React-PDF与CockroachDB的集成提供了强大而灵活的PDF生成解决方案。关键最佳实践包括:

  1. 分离关注点:将数据获取、模板设计和PDF渲染分离
  2. 错误处理:全面处理数据库错误和PDF渲染异常
  3. 安全性:保护数据库凭证,验证所有用户输入
  4. 测试:为PDF模板和数据处理逻辑编写单元测试
  5. 文档:维护清晰的API文档和模板说明

通过这种架构,您可以构建可靠、可扩展的PDF生成系统,满足从简单报表到复杂文档的各种需求。

要了解更多细节,请参考:

【免费下载链接】react-pdf 📄 Create PDF files using React 【免费下载链接】react-pdf 项目地址: https://gitcode.com/gh_mirrors/re/react-pdf

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐