Laravel 5.1におけるdownloadアクションの日本語ファイル名対応(The filename fallback must only contain ASCII characters.)と怒られた場合の対応

日本語ファイルがダウンロードできない

Laravelには便利なアクションがあって、以下のようなコードで簡単にファイルをダウンロードさせられるのだが 何かのコントローラの何かのアクション

public function test(Request $request)
{
    $filePath = '/var/www/hogehoge.zip';

    return response()->download($filePath);
}

以下だとThe filename fallback must only contain ASCII charactersと怒られてしまう。 何かのコントローラの何かのアクション

public function test(Request $request)
{
    $filePath = '/var/www/ほげほげ.zip';

    return response()->download($filePath);
    // InvalidArgumentException
}

これは内部で利用しているSymfonyのコンポーネントが引数をASCIIで受け取ることを前提としているせい。

どうすればいいのか

ちょっと前のLaravelであれば、訂正版:Laravelのエラー「The filename fallback must only contain ASCII characters」の対処法のようにして対応することができる。

しかし、Laravel 5.1ではマクロ周りの仕様が変わっており、このまま対応することはできない。

というわけで、5.1の仕様に則した方法をメモ。

まず、app\Providersあたりに”ResponseMacroServiceProvider”としてサービスプロバイダを作成。(別にプロバイダの名前はなんでもいい)

あとはおもむろに以下のように書く。 ResponseMacroServiceProvider.php

namespace App\Providers; // 名前空間を変更しているなら適宜直しましょう

use Illuminate\Support\Str;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Routing\ResponseFactory;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class ResponseMacroServiceProvider extends ServiceProvider
{

    public function boot(ResponseFactory $factory)
    {
        // ここではdownloadExとして登録していますが、何でも良いです
        $factory->macro('downloadEx', function($file, $name = null, array $headers = array(), $disposition = 'attachment'){
            $response = new BinaryFileResponse($file, 200, $headers, true);

            if (is_null($name))
            {
                $name = basename($file);
            }

            return $response->setContentDisposition($disposition, $name, Str::ascii($name));
        });
    }

    // registerは今回特に必要ないです
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        // TODO: Implement register() method.
    }
}

あとはconfig\app.phpにサービスプロバイダとして
ResponseMacroServiceProviderを追加。 app.php

return [
    // ~省略~
    'providers' => [
        // ~省略~
        App\Providers\ResponseMacroServiceProvider::class,
    ];
    // ~省略 ~
];

これで完了。あとは最初のコードをこんな感じにするだけ。 何かのコントローラの何かのアクション

public function test(Request $request)
{
    $filePath = '/var/www/ほげほげ.zip';

    return response()->downloadEx($filePath);
    // OK!
}

簡単で今までよりも見通しがよくなりました。バンザイ!

Strage downloadだと全角ファイル名がダメそう

 public function downloadFile(Request $request)
 {
    $disk = Storage::disk('s3');
    // 1.ファイルの名前をURIや、リクエストなどで渡す
    $file_name = $request->input('file_name')
        // 2.アップロードしたいディレクトリのパス
    $s3_dir_pash = 'upload/';
        // 1と2をあわせて、アップロード先パスを指定
    $s3_file_pash = $s3_dir_pash.$file_name; //'upload/5007.jpg'
    // ダウンロードする際のファイル名を、Content-Dispositionで指定
    $headers = [
        'Content-Type' => 'application/pdf',
        'Content-Disposition' => 'attachment; filename="' . $file_name . '"'
    ];
 
     return \Response::make($disk->get($s3_file_pash), 200, $headers);
     //$disk->download($s3_file_pash, $file_name, $headers);
     //↑の書き方でも良いが、日本語のファイル名でダウロードすると、The filename fallback must only contain ASCII characters.になってしまう。
 }

fromURL:http://sagatto.com/20190130_laravel_s3_file_operation

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク