JavaScript - express + bodyParser + puppeteer - JavaScript を動作させる


 クラウディア


1. 概要
2. 状況
3. server.js
4. 呼び出し側
5. 参考サイト

1. 概要

 説明が、なかなか難しい。

2. 状況

 もともと、あるコンテンツを「HTML」表示したものをバックで、「.pdf」化するというのが命題なのですが。  そのコンテンツが、中で、「Vue.js」を動作させているのです。  前ページの状態だと、コンテンツ表示の際の、「Vue.js」が動作しないので、「.pdf」化したものは、真っ白のものができあがります。  コンテンツ内の「Vue.js」まで動作させるには、もうひと工夫必要です。

3. server.js

 「server.js」の記述は、こうなります。

const express = require('express');
const bodyParser = require('body-parser');
const puppeteer = require('puppeteer');

const app = express();
app.use(bodyParser.json({ limit: '50mb' }));

app.post('/generate-pdf', async (req, res) =>
{
    const url = req.body.url;
    if (!url) return res.status(400).send('URL is required');

	process.env.HOME 			= '/path';
	process.env.XDG_CONFIG_HOME = '/path/.config';
	process.env.XDG_DATA_HOME   = '/path/.local/share';

    let browser;
    try {
        browser = await puppeteer.launch({
            headless: 'new',
			executablePath: '/usr/bin/chromium-browser',
            args: ['--no-sandbox', '--disable-setuid-sandbox'],
			userDataDir: '/path/.chromium', // apache が書き込み可能な場所
        });

        const page = await browser.newPage();
        await page.goto(url, {
            waitUntil: 'networkidle0', // Vue や Ajax が落ち着くまで待つ
            timeout: 60000
        });

        const pdfBuffer = await page.pdf({
            format: 'A4',
            landscape: true,
            printBackground: true,
            margin: {
                top: '20mm',
                bottom: '20mm',
                left: '15mm',
                right: '15mm'
            }
        });

        await browser.close();
        res.setHeader('Content-Type', 'application/pdf');
        res.setHeader('Content-Disposition', 'attachment; filename="document.pdf"');
        res.send(pdfBuffer);

    } catch (error) {
        if (browser) await browser.close();
        console.error(error);
        res.status(500).send('PDF generation failed');
    }
});

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Puppeteer server running on port ${PORT}`);
});
 前ページは、入力引数の「req」の「req.body.html」コンテンツのみ引っ張り出して、ブラウザに表示させていますが。  本ページの場合、入力引数は、コンテンツのリクエスト自体をブラウザで展開させています。  展開するので、ワークディレクトリが必要になるので、13~15行目や、23行目で、ディレクトリを定義しています。  さらに、「JavaScript」や「Vue.js」が動作する時間を 28~30行目で待って、落ち着いてから、「.pdf」化しているわけです。

4. 呼び出し側

 呼び出し側も前ページでは。

$html = view('any_page')->render();

// Node.js Puppeteer サーバーに送信
$response = Http::post('http://127.0.0.1:3000/generate-pdf', [
	'html' => $html
]);
 と、「html」部分を渡していましたが・・・。  本ページの方式の場合、だいぶ複雑になります。  「Laravel」でやるときは、まず、「node server.js」内で表示させるコンテンツのルートを定義します。

routes/web.php
 内に下記のようなルートを定義します。

	Route::get('/hogehoge_root', [ HogehogeController::class, 'viewHogehoge' ])->name('piyopiyo');
 呼び出し側で、「server.js」アクセス時に。

$params =
[
	'param1'	=> $param1,
	'param2'	=> $param2,
];

$url = route('piyopiyo', $params);
$response = Http::post('http://127.0.0.1:3000/generate-pdf', [ 'url' => $url, ]);
 てなコードを書きます。  ここ「$url = route(...)」と、「$response = Http::post(...)」の行は、わけて記述しないと。  呼び出し先に、うまくパラメータがわたりません。  呼び出し先の。下記のファイル内に。

app/Http/Controllers/Api/HogehogeController.php
 下記のようなコードを記述します。

public function viewHogehoge(Request $request)
{
	try
	{
		$params = (object)$request->all();

		$param1 = $params->param1;
		$param2 = $params->param2;

		return view('view_blade', compact(
			'param1',
			'param2',
		));
	}
	catch (Exception $ex)
	{
		logger($ex->getMessage());
		throw $ex;
	}
}
 これで、「server.js」の先で、「javascript」「Vue.js」が動作した後、「.pdf」化することになります。

5. 参考サイト

 本ページは、「ChatGPT」くんを参考にさせていただきました。

AbemaTV 無料体験
U-NEXT