注意
この記事はPDFが0バイトになってしまうケースを網羅的に解説します。
途中で問題が見つかったら、すぐに対策チェックリストで確認してみてください。
PDF生成プロセスの概要
Webアプリやデスクトップアプリでよく使われるPDF生成の流れ
- データ取得(DB、API、フォーム入力など)
- レイアウト設計(テンプレート、CSS、フォント)
- PDFエンジン呼び出し(
wkhtmltopdf,ReportLab,TCPDFなど)- ストリーム/ファイル書き込み
- クライアントへ返却(ダウンロードリンク, 直接保存)
このうち「4. ストリーム/ファイル書き込み」に失敗すると、生成されたファイルは0バイトになることが多いです。
0バイトになる主な原因
| 原因 | 典型的な症状 | 主な原因 |
|---|---|---|
| ファイルパス/ディレクトリ名の不正 | ファイルが作成できない、名前が重複 | ../ を含むパス、特殊文字、長すぎるパス |
| 書き込み権限が不足 | 生成失敗、エラーログに Permission denied |
ファイル/フォルダに実行ユーザーが書き込み不可 |
| PDFライブラリのバージョン不整合 | 例外が投げられる、途中で終了 | 依存ライブラリの旧版、C++/Python バイナリの不一致 |
| メモリ不足 | 生成途中でクラッシュ、OOM エラー |
大容量画像/テキストを一括読み込む |
| テンプレートやCSSのパース失敗 | PDFが空になる、スタイルが崩れる | HTML/CSS の構文エラー |
| ストリームバッファのフラッシュ漏れ | 0バイトファイル | flush() していない、close() していない |
| 一時ファイルの削除タイミング | 0バイトになる、存在しない | 生成途中で一時ファイルを削除してしまうコード |
| 非同期処理の競合 | 途中でファイルが削除される、競合状態 | async / await でファイル書き込み前に次の処理が走る |
| 環境変数の設定ミス | ランタイムで例外が発生 | フォントパス、PATH の設定漏れ |
| エラーハンドリングの不備 | 例外が捉えられず 0バイトファイル生成 | 例外時にファイル削除処理を行わない、ログが出ない |
ポイント
PDFが0バイトになるのは「書き込み前に失敗」していることがほとんど。
したがって、エラーログやスタックトレースを必ず確認しましょう。
対策・解決策
1. パスと権限のチェック
# Linux での例
ls -ld /var/www/app/tmp
# または
stat -c "%A %U" /var/www/app/tmp/file.pdf
-
パスを正規化(
realpathで絶対パス化) -
ユーザー権限:Apache/Nginxは通常
www-dataで走る。
chown -R www-data:www-data /var/www/app/tmp -
パーミッション:
chmod 775 /var/www/app/tmp
2. PDFライブラリのバージョンとキャッシュのクリア
- 公式ドキュメントで推奨バージョンを確認
- 依存関係の更新後、キャッシュをクリア
rm -rf vendor/cache # PHPの場合 pip cache purge # Pythonの場合
3. メモリ・バッファ管理
- 大きな画像は 圧縮 してから投入
- バッファリングを利用し 一度に書き込わない
with open(path, "wb") as f: f.write(pdf_bytes) # pdf_bytes は bytearray -
ストリームフラッシュ:
f.flush()を呼ぶ -
生成時のログ:
--verboseオプションで詳細ログ取得
4. テンプレートエラーの検証
- HTML/CSS を 独立でブラウザで表示 →
Ctrl+U - HTML Linter (htmlhint, tidy) でエラーを洗い出す
- PDF生成時の例外メッセージをキャッチし、テンプレートの失敗箇所を特定
5. エラーハンドリングとログ
try {
$pdf = $pdfEngine->render($template, $data);
file_put_contents($filePath, $pdf);
} catch (Exception $e) {
error_log("PDF生成失敗: " . $e->getMessage());
// 既に作成されているか0バイトなら削除
if (file_exists($filePath) && filesize($filePath) == 0) {
unlink($filePath);
}
throw $e; // 必要ならフロントに表示
}
6. 一時ファイル管理と同期
-
一時ディレクトリは OS が管理(
tempfile/tmpfile) -
tempfile()で扱えば 自動削除が保証される -
asyncで作成した場合は wait してから次処理
PDF生成時チェックリスト
| # | チェック項目 | 実行手順 | 期待結果 |
|---|---|---|---|
| 1 | パスの有効性 | realpath() で存在確認 |
失敗なら修正 |
| 2 | ディレクトリ・ファイル権限 | ls -l で確認 |
777 付与/所有者を適切に |
| 3 | 書き込みテスト | 空ファイルを作成→削除 | 書き込み OK |
| 4 | ライブラリバージョン | composer show, pip list |
公式バージョンと一致 |
| 5 | 依存ファイルの存在 | docker ps, ldd |
必要 DLL が見つかる |
| 6 | テンプレート検証 | ブラウザで確認 | 例外無し |
| 7 | メモリ使用量 | ps, top |
1-2GB 以内 |
| 8 | バイト列・サイズチェック | filesize() |
非ゼロ |
| 9 | ログ確認 | tail -f error.log |
エラー無し |
| 10 | 再現性テスト | 5 回生成 | 一貫して生成 |
備考
1 から 4 は「事前にセットアップ」を確実に。
5 以降は「実行時」にチェックリストを実行し、失敗箇所を特定します。
さらに先へ:自動化と監視
CI で PDF 生成確認
-
GitHub Actions に「PDF生成テスト」を追加
- name: Generate PDF run: | npm run test:pdf mv output.pdf result.pdf - name: Upload PDF artifact uses: actions/upload-artifact@v3 with: name: pdf-result path: result.pdf
アプリ監視
-
Prometheus で
pdf_generation_duration_msのメトリクス -
alertmanagerで「PDF生成が0バイト時にアラート」
エラーレポート用に UI で表示
<div class="pdf-error" style="color:red;">
PDF の生成に失敗しました。<br>
詳細: {{ error_message }}
</div>
まとめ
- **0バイト PDF は「書き込み前に失敗」**という強いシグナル。
- 原因は「パス/権限」「ライブラリ」「テンプレート」「メモリ」「バッファ」等に集約。
- 対策チェックリストで一つずつ検証すれば、再発を防止できます。
- さらに CI/CI の自動化や監視を組み合わせれば、「いつ・どこで」問題が起きたかを即座に把握可能です。
ぜひ、上記リストを日常の開発フローに組み込んで、PDF生成の安定化を図ってください。


コメント