- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2018-10-01T11:00:04+00:00","","")
* サーバ側で作成したPDFの表示とクライアントPCへの保存時のファイル名の指定 [#a9ba6dfa]
#setlinebreak(on)
#contents
関連 : [[サーブレットでファイルダウンロード]]
** やりたいこと [#p3afb117]
+サーバ側でPDFを生成し、そのままブラウザウィンドウ内に表示する。(開く or 保存のダイアログを出さずに)
+表示したPDFファイルをクライアントに保存する時のファイル名を指定する。
#html(<div style="padding-left:10px">)
上記(1) は Content-Disposition に inline を指定するだけで実現可能なので、何も難しい事はない。※ただし Content-Type の指定は必須。
上記(2) はブラウザ環境によって挙動が異なるため、けっこう難問。※調べて分かったが問題なのは IE だけ。
#html(</div>)
#html(<table style="margin:0px 0px 20px 0px;"><tr><td>)
#html(<p style="padding-left:40px;">ブラウザに表示したPDFを保存する時に・・</p>)
#ref(exp1.png,nolink)
#html(</td><td align="left">)
#html(<p style="padding-left:40px;">↓ こうではなくて・・</p>)
#ref(exp2.png,nolink)
#html(<p style="padding-left:40px;">↓ こうなって欲しい</p>)
#ref(exp3.png,nolink)
#html(</td></tr></table>)
** 問題点 [#ve7c5791]
+クライアント環境(ブラウザ)によっては、Content-Disposition が inline の時は、指定した filename が効かない場合がある。
+ブラウザによってファイル名のエンコードが必要な場合と、逆にエンコードしない方が良い場合がある。
以降は、特に問題点(1)について記載する。
** どのブラウザが問題? [#z0cf9c5b]
#html(<div style="padding:0px 0px 10px 10px;">)
Content-Disposition が inline 時に filename が効かないブラウザがどれかを調べてみた。(ついでにファイル名のエンコードも)
※ バージョンが - のものは 2013年2月時点の最新を使用。
※ PDFリーダーは2013年2月時点の最新を使用。
#html(</div>)
|OS|ブラウザ|バージョン|inline時のfilename|日本語ファイル名のエンコード|h
|WinXP|Internet Explorer|6|&color(red){無効};|&color(red){エンコードが必要};|
|WinXP|Internet Explorer|7|&color(red){無効};|&color(green){どちらでもOK};|
|WinXP|Internet Explorer|8|&color(red){無効};|&color(green){どちらでもOK};|
|Win7|Internet Explorer|9|&color(red){無効};|&color(green){どちらでもOK};|
|WinXP/7|Google Chrome|-|&color(green){有効};|&color(green){どちらでもOK};|
|WinXP/7|Mozilla Firefox|-|&color(green){有効};|&color(red){エンコードしちゃダメ};|
|Mac|Safari|-|&color(green){有効};|&color(red){エンコードしちゃダメ};|
|Mac|Google Chrome|-|&color(green){有効};|&color(green){どちらでもOK};|
|Mac|Mozilla Firefox|-|>|面倒くさそうなので省略&br;(アドオン設定が正しくされていればたぶんWindowsと同じ)|
#html(<div style="padding-left:10px;">)
こうやって見ると、やっぱり Google Chrome はかなり優秀。
IEはどのバージョンでも filename 属性は無効。※IE10は調べてないけど変わってるのかな?
※検証に使用したコード : http://www.magata.net/test/download_pdf/
#html(</div>)
** Content-Disposition で指定したfilenameが有効な環境の場合 [#k3406116]
#html(<div style="padding-left:10px;">)
以下のとおり実装する。特に難しい所はなし。
#html(</div>)
#html(<div style="padding-left:10px;">)
*** 実装サンプル(PHPの場合) [#q72eedb7]
#mycode2(){{
// 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;
}}
#html(</div>)
** Content-Disposition で指定したfilenameが無効な環境の場合 [#kd108b65]
#html(<div style="padding-left:10px;">)
この場合は、HTTPヘッダで指定したファイル名でなく、その時に表示しているURLが保存時のファイル名になってしまうので、少し工夫が必要になる。
《案1》
Content-Disposition に attachment を指定し、いったんダウンロードダイアログを表示する。
※いちどダウンロードダイアログを表示すると、filename 属性が有効な状態で開くので、
開いた後に保存をした場合にも filename で指定したファイル名で保存ができる。
《案2》
リダイレクトと mode_rewrite などを駆使して、URLが保存時のファイル名になるようにする。
案1で妥協できる場合は、inline を attachment に変えるだけなので、詳細は省略。
以降では、案2について記載するが、ここまでやりますか?という感じがしなくもない。
*** 実装イメージ [#l5e84f98]
#html(<div style="padding:10px 0px 10px 10px;">)
保存時に使用したいファイル名がURLになるように、リクエストを行うようにする(リダイレクトさせる)。
つまり、 xxxx.pdf にアクセスした時に、PDF生成/取得処理が行われるようにする。
ただし、そのまま xxxx.pdf にアクセスすると、xxxx.pdf をそのまま表示してしまうので、
*.pdf へのリクエストを PDF取得用のプログラムのPATHに動的に変換する。
(下記では mod_rewrite を使用しているが、JAVAなどの場合は web.xml に同じ意味合いの記述を行っても良い。)
#html(</div>)
#ref(image.png,nolink)
#html(<div style="padding-left:40px;">)
上図の(C) の時点で、ブラウザがアクセスしている URL は xxxx.pdf となるので、
表示しているページを保存しようとした場合には、xxxx.pdf というファイル名で保存を行う事ができる。
※ サーバ上にpdfを生成して直接アクセスしても良いけど、マルチユーザを考慮し、動的処理にしたうえでセッションを利用。
※ load_pdf.php で全部やっても良いけど、生成するファイルの内容に応じてファイル名を動的に変えたいケースを考慮して、生成と取得を分けてみた。
#html(</div>)
*** 実装サンプル(PHPの場合) [#q72eedb7]
#html(<div style="padding-left:10px;">)
※デモ・サンプル : http://www.magata.net/test/download_pdf/
#html(</div>)
#html(<p style="padding-left:10px;">create_pdf.php</p>)
#mycode2(){{
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);
}}
#html(<p style="padding-left:10px;">.htaccess (mod_rewriteの定義)</p>)
#mycode2(){{
RewriteEngine On
# PATH変換(pdf -> php)
RewriteRule ^.*\.pdf$ load_pdf.php [L]
}}
#html(<p style="padding-left:10px;">load_pdf.php</p>)
#mycode2(){{
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;
}}
#html(</div>)
上記までで、とりあえず問題点(1)は解決。