サーバ側で作成したPDFの表示とクライアントPCへの保存時のファイル名の指定

関連 : サーブレットでファイルダウンロード

やりたいこと

  1. サーバ側でPDFを生成し、そのままブラウザウィンドウ内に表示する。(開く or 保存のダイアログを出さずに)
  2. 表示したPDFファイルをクライアントに保存する時のファイル名を指定する。
《メモ》
上記(1) は Content-Disposition に inline を指定するだけで実現可能なので、何も難しい事はない。※ただし Content-Type の指定は必須。
上記(2) はブラウザ環境によって挙動が異なるため、けっこう難問。※調べて分かったが問題なのは IE だけ。

ブラウザに表示したPDFを保存する時に・・

#ref(): File not found: "exp1.png" at page "サーバ側で作成したPDFの表示とクライアントPCへの保存時のファイル名の指定"

↓ こうではなくて・・

#ref(): File not found: "exp2.png" at page "サーバ側で作成したPDFの表示とクライアントPCへの保存時のファイル名の指定"

↓ こうなって欲しい

#ref(): File not found: "exp3.png" at page "サーバ側で作成したPDFの表示とクライアントPCへの保存時のファイル名の指定"

問題点

  1. クライアント環境(ブラウザ)によっては、Content-Disposition が inline の時は、指定した filename が効かない場合がある。
  2. ブラウザによってファイル名のエンコードが必要な場合と、逆にエンコードしない方が良い場合がある。

 以降は、特に問題点(1)について記載する。

どのブラウザが問題?

Content-Disposition が inline 時に filename が効かないブラウザがどれかを調べてみた。(ついでにファイル名のエンコードも)
※ バージョンが - のものは 2013年2月時点の最新を使用。
※ PDFリーダーは2013年2月時点の最新を使用。

OSブラウザバージョンinline時のfilename日本語ファイル名のエンコード
WinXPInternet Explorer6無効エンコードが必要
WinXPInternet Explorer7無効どちらでもOK
WinXPInternet Explorer8無効どちらでもOK
Win7Internet Explorer9無効どちらでもOK
WinXP/7Google Chrome-有効どちらでもOK
WinXP/7Mozilla Firefox-有効エンコードしちゃダメ
MacSafari-有効エンコードしちゃダメ
MacGoogle Chrome-有効どちらでもOK
MacMozilla Firefox-面倒くさそうなので省略
(アドオン設定が正しくされていればたぶんWindowsと同じ)

こうやって見ると、やっぱり Google Chrome はかなり優秀。
IEはどのバージョンでも filename 属性は無効。※IE10は調べてないけど変わってるのかな?

※検証に使用したコード : http://www.magata.net/test/download_pdf/

Content-Disposition で指定したfilenameが有効な環境の場合

以下のとおり実装する。特に難しい所はなし。

実装サンプル(PHPの場合)

// PDFの作成
$data = pdfデータを作成する関数();

// PDFファイル名の指定
pdf_fname = urlencode("xxxx.pdf");

// HTTPヘッダを出力
header("Content-Type: application/pdf");
header("Content-Disposition: inline;filename=\"${pdf_fname}\"");

// PDFデータを出力
echo $data;
exit;

Content-Disposition で指定したfilenameが無効な環境の場合

この場合は、HTTPヘッダで指定したファイル名でなく、その時に表示しているURLが保存時のファイル名になってしまうので、少し工夫が必要になる。

《案1》
 Content-Disposition に attachment を指定し、いったんダウンロードダイアログを表示する。
 ※いちどダウンロードダイアログを表示すると、filename 属性が有効な状態で開くので、
  開いた後に保存をした場合にも filename で指定したファイル名で保存ができる。

《案2》
 リダイレクトと mode_rewrite などを駆使して、URLが保存時のファイル名になるようにする。

案1で妥協できる場合は、inline を attachment に変えるだけなので、詳細は省略。
以降では、案2について記載するが、ここまでやりますか?という感じがしなくもない。

実装イメージ

保存時に使用したいファイル名がURLになるように、リクエストを行うようにする(リダイレクトさせる)。
つまり、 xxxx.pdf にアクセスした時に、PDF生成/取得処理が行われるようにする。
ただし、そのまま xxxx.pdf にアクセスすると、xxxx.pdf をそのまま表示してしまうので、
*.pdf へのリクエストを PDF取得用のプログラムのPATHに動的に変換する。
(下記では mod_rewrite を使用しているが、JAVAなどの場合は web.xml に同じ意味合いの記述を行っても良い。)

#ref(): File not found: "image.png" at page "サーバ側で作成したPDFの表示とクライアントPCへの保存時のファイル名の指定"

上図の(C) の時点で、ブラウザがアクセスしている URL は xxxx.pdf となるので、
表示しているページを保存しようとした場合には、xxxx.pdf というファイル名で保存を行う事ができる。
※ サーバ上にpdfを生成して直接アクセスしても良いけど、マルチユーザを考慮し、動的処理にしたうえでセッションを利用。
※ load_pdf.php で全部やっても良いけど、生成するファイルの内容に応じてファイル名を動的に変えたいケースを考慮して、生成と取得を分けてみた。

実装サンプル(PHPの場合)

※デモ・サンプル : http://www.magata.net/test/download_pdf/

create_pdf.php

session_start();

// PDFの作成
$data = PDFを作成する関数();

// PDFをセッションに格納
$_SESSION["download_pdf"] = $data;

// リダイレクト
$pdf_fname = urlencode("xxxx.pdf");
header("Location: " . "http://" . $_SERVER["HTTP_HOST"] . "/test/download_pdf/" . $pdf_fname);

.htaccess (mod_rewriteの定義)

RewriteEngine On

# PATH変換(pdf -> php)
RewriteRule ^.*\.pdf$ load_pdf.php [L] 

load_pdf.php

session_start();

// HTTPヘッダに指定するPDFファイル名( filenameが有効な環境用)
$pdf_fname = urlencode("xxxx.pdf");

// セッションに退避しておいたPDFデータを出力
header("Content-Type: application/pdf");
header("Content-Disposition: inline;filename=\"${pdf_fname}\"");
echo $_SESSION["download_pdf"];
exit;

上記までで、とりあえず問題点(1)は解決。


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS