#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)は解決。