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

101 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をインポート
// LibreOffice AppImageのパス
const libreofficeAppImage = './modules/LibreOffice-still.standard-x86_64.AppImage';
// tmpディレクトリのパス
const tmpDir = path.join(__dirname, 'tmp');
// tmpディレクトリが存在しない場合は作成
if (!fs.existsSync(tmpDir)) {
fs.mkdirSync(tmpDir);
}
// POSTエンドポイント
module.exports = (app) => {
app.post('/pdfConvert', 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キーが不正です' });
}
const { base64, fileName } = req.body;
if (!base64 || !fileName) {
return res.status(400).json({ error: 'base64とfileNameは必須です' });
}
const pdfBuffer = await convertBase64ToPdf(base64, 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 convertBase64ToPdf(base64Data, originalFileName) {
const tempId = uuidv4(); // 一意のIDを生成
const ext = path.extname(originalFileName) || '.tmp'; // 拡張子を取得、なければ.tmpを使用
const orgPath = path.join(tmpDir, `${tempId}${ext}`); // 一時的なファイルのパス
const pdfPath = path.join(tmpDir, `${tempId}.pdf`);
// base64データをファイルに保存
fs.writeFileSync(orgPath, Buffer.from(base64Data, 'base64'));
// LibreOfficeでPDF変換タイムアウト60秒
await new Promise((resolve, reject) => {
const child = execFile(
libreofficeAppImage,
[
'--headless',
'--convert-to', 'pdf',
'--outdir', tmpDir,
orgPath
],
(error, stdout, stderr) => {
if (error) {
console.error('stderr:', stderr);
reject(new Error(`LibreOfficeエラー: ${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(pdfPath)) {
throw new Error('PDF変換に失敗しました');
}
const pdfBuffer = fs.readFileSync(pdfPath);
// cleanup
try {
if (fs.existsSync(orgPath)) fs.unlinkSync(orgPath);
if (fs.existsSync(pdfPath)) fs.unlinkSync(pdfPath);
} catch (cleanupError) {
console.error('クリーンアップエラー:', cleanupError.message);
}
return pdfBuffer;
}