const express = require('express'); const { chromium } = require('playwright'); const cors = require('cors'); const bodyParser = require('body-parser'); const path = require('path'); const fs = require('fs'); const app = express(); const PORT = process.env.PORT || 3000; // 中间件 app.use(cors()); app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' })); // 确保输出目录存在 const outputDir = path.join(__dirname, 'output'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } class PdfService { constructor() { this.browser = null; } async initialize() { if (this.browser) return; this.browser = await chromium.launch({ headless: true, args: [ '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--disable-web-security' ] }); console.log('Playwright浏览器初始化成功'); } async generatePdf(options) { if (!this.browser) { await this.initialize(); } const context = await this.browser.newContext(); const page = await context.newPage(); try { const { html, url, landscape = false, format = 'A4', margin = { top: 20, right: 20, bottom: 20, left: 20 }, printBackground = true, timeout = 60000 } = options; page.setDefaultTimeout(timeout); if (url) { console.log(`正在生成PDF: ${url}`); await page.goto(url, { waitUntil: 'networkidle', timeout: timeout }); } else if (html) { console.log('正在转换HTML到PDF'); await page.setContent(html, { waitUntil: 'networkidle', timeout: timeout }); } else { throw new Error('必须提供html或url参数'); } // 生成PDF const pdfBuffer = await page.pdf({ landscape, format, margin, printBackground, preferCSSPageSize: false }); return pdfBuffer; } finally { await context.close(); } } async close() { if (this.browser) { await this.browser.close(); } } } const pdfService = new PdfService(); // 健康检查端点 app.get('/health', async (req, res) => { try { await pdfService.initialize(); res.json({ status: 'healthy', service: 'HTML to PDF Service', timestamp: new Date().toISOString() }); } catch (error) { res.status(503).json({ status: 'unhealthy', error: error.message }); } }); // 通过URL生成PDF app.get('/generate', async (req, res) => { try { const { url, download = 'false' } = req.query; if (!url) { return res.status(400).send(`
请提供URL参数,例如:
/generate?url=https://example.com
`);
}
const pdfBuffer = await pdfService.generatePdf({
url: url,
timeout: 60000
});
const filename = `document-${Date.now()}.pdf`;
res.setHeader('Content-Type', 'application/pdf');
if (download === 'true') {
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
} else {
res.setHeader('Content-Disposition', `inline; filename="${filename}"`);
}
res.send(pdfBuffer);
} catch (error) {
console.error('PDF生成错误:', error);
res.status(500).send(`
错误信息: ${error.message}
返回首页 `); } }); // 通过HTML内容生成PDF - POST接口 app.post('/generate/html', async (req, res) => { try { const { html, download = false, filename = `document-${Date.now()}.pdf` } = req.body; if (!html) { return res.status(400).json({ success: false, error: 'HTML内容不能为空' }); } console.log('正在通过HTML内容生成PDF'); const pdfBuffer = await pdfService.generatePdf({ html: html, timeout: 60000 }); res.setHeader('Content-Type', 'application/pdf'); if (download) { res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); } else { res.setHeader('Content-Disposition', `inline; filename="${filename}"`); } res.send(pdfBuffer); } catch (error) { console.error('HTML内容转PDF错误:', error); res.status(500).json({ success: false, error: error.message }); } }); // 通过HTML内容生成PDF - GET接口(表单提交) app.get('/generate/html-form', (req, res) => { res.send(`通过网页URL生成PDF文档
URL转PDF: GET /generate?url=https://example.com
HTML转PDF: POST /generate/html
// 请求示例
{
"html": "<h1>Hello PDF</h1>",
"download": true,
"filename": "document.pdf"
}