関連 : サーブレットでファイルダウンロード
上記(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 が効かないブラウザがどれかを調べてみた。(ついでにファイル名のエンコードも)
※ バージョンが - のものは 2013年2月時点の最新を使用。
※ PDFリーダーは2013年2月時点の最新を使用。
OS | ブラウザ | バージョン | inline時のfilename | 日本語ファイル名のエンコード |
WinXP | Internet Explorer | 6 | 無効 | エンコードが必要 |
WinXP | Internet Explorer | 7 | 無効 | どちらでもOK |
WinXP | Internet Explorer | 8 | 無効 | どちらでもOK |
Win7 | Internet Explorer | 9 | 無効 | どちらでもOK |
WinXP/7 | Google Chrome | - | 有効 | どちらでもOK |
WinXP/7 | Mozilla Firefox | - | 有効 | エンコードしちゃダメ |
Mac | Safari | - | 有効 | エンコードしちゃダメ |
Mac | Google Chrome | - | 有効 | どちらでもOK |
Mac | Mozilla Firefox | - | 面倒くさそうなので省略 (アドオン設定が正しくされていればたぶんWindowsと同じ) |
こうやって見ると、やっぱり Google Chrome はかなり優秀。
IEはどのバージョンでも filename 属性は無効。※IE10は調べてないけど変わってるのかな?
※検証に使用したコード : http://www.magata.net/test/download_pdf/
以下のとおり実装する。特に難しい所はなし。
// 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;
この場合は、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 で全部やっても良いけど、生成するファイルの内容に応じてファイル名を動的に変えたいケースを考慮して、生成と取得を分けてみた。
※デモ・サンプル : 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)は解決。