CakePHP3.6で開発を行っていると、ファイル名にマルチバイトを含むファイルをダウンロードするとブラウザによっては、正しいファイル名でダウンロードされません。そういった経験はないでしょうか。
今回はその回避策を紹介します。
Macが遅い&重い時の改善・対処方法とは?動作がおかしくなった場合の対処とメンテナンス方法(完全保存版)
MacのExcel(エクセル)が重い・クラッシュしてしまう時の対処方法
MacのWord(ワード)が重い・クラッシュしてしまう時の対処方法
条件
ファイル名 | マルチバイトを含むファイル名 |
ファイルの中身 | CSVデータ |
拡張子 | hoge(独自拡張子) |
ブラウザ | Edge、Internet Explorer 11、Chrome、Safari、Firefox ※2018年09月現在最新 |
ソース
public function download()
{
//レンダリングを解除
$this->autoRender = false;
//ダウンロードファイルのファイル名
//※hoge:CSVデータ。独自拡張子
$fileName = 'マルチバイトファイル.hoge';
//ダウンロードファイルのパス
$path = "/tmp/$fileName";
//User-Agentの取得
$userAgent = strtolower(env('HTTP_USER_AGENT'));
//Edge、Internet Explorerに対応
if (strpos($userAgent , 'edge') || strpos($userAgent , 'trident') || strpos($userAgent , 'msie'))
{
//ファイル名をURLエンコードする
$fileName = rawurlencode($fileName);
}
//Locationを指定する
setlocale(LC_ALL, 'ja_JP.UTF-8');
//CakePHP3のResponseに設定する
$response = $this->response->withFile(
$path, [
'name' => $fileName,
'download' => true
]
);
//Safari対応
if (strpos($userAgent , 'edge') === false &&
strpos($userAgent , 'trident') === false &&
strpos($userAgent , 'msie') === false &&
strpos($userAgent , 'chrome') === false &&
strpos($userAgent , 'firefox') === false &&
strpos($userAgent , 'safari'))
{
//mimeTypeによって勝手に拡張子が付与されるのを回避
$response = $response->withType('text/hoge');
}
else
{
//その他のブラウザの場合は中身がCSVで有ることを明記
$response = $response->withType('text/csv');
}
return $response;
}
解説
上記のソースのポイントです。
Edge、Internet Explorer11でダウンロードしたファイル名が文字化けする
if (strpos($userAgent , 'edge') || strpos($userAgent , 'trident') || strpos($userAgent , 'msie'))
{
$fileName = rawurlencode($fileName);
}
EdgeやInternet Explorer 11からファイルをダウンロードする場合、
マニュアルどおりに実装すると、マルチバイトの部分が文字化けしたファイル名でダウンロードされます。
その文字化けを回避するには、User-AgentからアクセスしたブラウザがEdgeなのかInternet Explorer 11なのか判別し、ダウンロード時のファイル名をURLエンコードする必要があります。
SplFileInfoの5C問題
setlocale(LC_ALL, 'ja_JP.UTF-8');
$this->response->withFileの処理内で呼ばれる「SplFileInfo」は、マルチバイトを含むファイル名が正しく取得できません。
そのため、ダウンロード対象のファイルが存在しているにもかかわらず、取得したファイル名が正しくないためファイルの存在チェックで失敗します。
ファイル名を正しく取得し、この事象を回避するには、ロケールを『UTF-8』に設定することで、マルチバイトを含むファイル名も正しく取得できるようになり、ファイルの存在チェックが正しく行われるようになります。
独自拡張子への対応
if (strpos($userAgent , 'edge') === false &&
strpos($userAgent , 'trident') === false &&
strpos($userAgent , 'msie') === false &&
strpos($userAgent , 'chrome') === false &&
strpos($userAgent , 'firefox') === false &&
strpos($userAgent , 'safari'))
{
$response = $response->withType('text/hoge');
}
else
{
$response = $response->withType('text/csv');
}
独自の拡張子を使用する場合、注意が必要です。
SafariではダウンロードするファイルのMimeに対応する拡張子が付いていない場合、自動的に付加する仕様になっています。
また、CakePHP3.6では明示的にMimeを指定しない場合、『text/html』がMimeとして設定されるため、Safariでダウンロードすると「.html」が必ず付加されてしまいます。
この「.html」の付与を回避するには、自動で付加されないようにMimeを明示的に設定し、独自拡張子のままダウンロードできるようにする必要があります。