ken_nogi/NodeJS/express/modules/pdfMerge.js
Kenichiro NOGI 51a95c5e21 NodeJS
2025-07-10 10:12:37 +09:00

98 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs');
const path = require('path');
const { execFile } = require('child_process');
const { v4: uuidv4 } = require('uuid');
const express = require('express'); // JSON解析用にexpressをインポート
const { API_KEY } = require('../config.js'); // API_KEYをインポート
// tmpディレクトリのパス
const tmpDir = path.join(__dirname, 'tmp');
// tmpディレクトリが存在しない場合は作成
if (!fs.existsSync(tmpDir)) {
fs.mkdirSync(tmpDir);
}
// POSTエンドポイント
module.exports = (app) => {
app.post('/pdfMerge', express.json({ limit: '30mb' }), async (req, res) => {
try {
// APIキーのチェック
const apiKey = req.headers['x-api-key'];
if (!apiKey || apiKey !== API_KEY) {
console.log('APIキーが不正です');
return res.status(401).json({ error: 'APIキーが不正です' });
}
// base64_1, base64_2, fileName の3つをチェック
const { base64_1, base64_2, fileName } = req.body;
if (!base64_1 || !base64_2 || !fileName) {
return res.status(400).json({ error: 'base64_1, base64_2, fileNameは必須です' });
}
const pdfBuffer = await mergeBase64ToPdf(base64_1, base64_2, fileName);
const safeFileName = encodeURIComponent(path.parse(fileName).name); // ファイル名をエスケープ
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename="${safeFileName}.pdf"`
});
res.send(pdfBuffer);
} catch (err) {
console.error('変換エラー:', err.message);
res.status(500).json({ error: err.message });
}
});
};
// PDF結合処理
async function mergeBase64ToPdf(base64Data1, base64Data2) {
const tempId = uuidv4(); // 一意のIDを生成
const orgPath1 = path.join(tmpDir, `${tempId}_1.pdf`);
const orgPath2 = path.join(tmpDir, `${tempId}_2.pdf`);
const mergedPath = path.join(tmpDir, `${tempId}_merged.pdf`);
// base64データをファイルに保存
fs.writeFileSync(orgPath1, Buffer.from(base64Data1, 'base64'));
fs.writeFileSync(orgPath2, Buffer.from(base64Data2, 'base64'));
// pdfuniteでPDF結合
await new Promise((resolve, reject) => {
const child = execFile(
'pdfunite',
[orgPath1, orgPath2, mergedPath],
(error, stdout, stderr) => {
if (error) {
console.error('stderr:', stderr);
reject(new Error(`pdfuniteエラー: ${stderr}`));
} else {
resolve();
}
}
);
const timeout = setTimeout(() => {
child.kill('SIGTERM'); // SIGTERMを使用してプロセスを終了
reject(new Error('PDF変換がタイムアウトしました60秒経過'));
}, 60000);
child.on('exit', () => clearTimeout(timeout));
});
// 結合されたPDFを読み込む
if (!fs.existsSync(mergedPath)) {
throw new Error('PDF結合に失敗しました');
}
const pdfBuffer = fs.readFileSync(mergedPath);
// cleanup
try {
if (fs.existsSync(orgPath1)) fs.unlinkSync(orgPath1);
if (fs.existsSync(orgPath2)) fs.unlinkSync(orgPath2);
if (fs.existsSync(mergedPath)) fs.unlinkSync(mergedPath);
} catch (cleanupError) {
console.error('クリーンアップエラー:', cleanupError.message);
}
return pdfBuffer;
}