ken_nogi/NodeJS/ExcelToPDF/ExcelToPdf.js
Kenichiro NOGI 51a95c5e21 NodeJS
2025-07-10 10:12:37 +09:00

110 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 express = require('express');
const { v4: uuidv4 } = require('uuid');
const https = require('https');
const cors = require('cors');
const app = express();
app.use(express.json({ limit: '20mb' })); // 大きなファイルにも対応
app.use(cors({
origin: [
'https://neo999.next-hd.net',
'https://nextoffice.next-hd.co.jp',
], // 許可するオリジン
optionsSuccessStatus: 200
})); // CORS対応
const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; // 任意のAPIキーを設定
// LibreOffice AppImageのパス
const libreofficeAppImage = './LibreOffice-still.standard-x86_64.AppImage';
// PDF変換処理
async function convertExcelBase64ToPdf(base64Data, originalFileName) {
const tempId = uuidv4();
const ext = path.extname(originalFileName) || '.xlsx';
const excelPath = path.join(__dirname, `${tempId}${ext}`);
const pdfPath = path.join(__dirname, `${tempId}.pdf`);
// base64データをファイルに保存
fs.writeFileSync(excelPath, Buffer.from(base64Data, 'base64'));
// LibreOfficeでPDF変換タイムアウト60秒
await new Promise((resolve, reject) => {
const child = execFile(
libreofficeAppImage,
[
'--headless',
'--convert-to', 'pdf',
'--outdir', __dirname,
excelPath
],
(error, stdout, stderr) => {
if (error) {
console.error('stderr:', stderr);
reject(error);
} else {
resolve();
}
}
);
// 60秒タイムアウト
const timeout = setTimeout(() => {
child.kill('SIGKILL');
reject(new Error('PDF変換がタイムアウトしました60秒経過'));
}, 60000);
child.on('exit', () => clearTimeout(timeout));
});
// PDFファイルを読み込む
if (!fs.existsSync(pdfPath)) {
// cleanup
fs.unlinkSync(excelPath);
throw new Error('PDF変換に失敗しました');
}
const pdfBuffer = fs.readFileSync(pdfPath);
// cleanup
fs.unlinkSync(excelPath);
fs.unlinkSync(pdfPath);
return pdfBuffer;
}
// POSTエンドポイント
app.post('/XlsxToPdfconvert', async (req, res) => {
try {
// APIキーの検証
const apiKey = req.headers['x-api-key'];
if (!apiKey || apiKey !== API_KEY) {
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 convertExcelBase64ToPdf(base64, fileName);
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename="${path.parse(fileName).name}.pdf"`
});
res.send(pdfBuffer);
} catch (err) {
console.error('変換エラー:', err.message);
res.status(500).json({ error: err.message });
}
});
// HTTPSサーバ起動
const PORT = 30308;
// HTTPS用証明書・秘密鍵のパス
const sslOptions = {
cert: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/fullchain.pem'),
key: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/privkey.pem')
};
https.createServer(sslOptions, app).listen(PORT, () => {
console.log(`HTTPSサーバー起動: https://localhost:${PORT}`);
});