Windows 10では、ファイル名として使用してはいけない文字がいくつかあります。入力しようとすると、「ファイル名に次の文字は使えません」とエラーになります。さて、ダウンロードのファイルにこれらの禁止文字が使用されていたらどうしましょう?

例えば、サーバーにアップされているファイル名は「567.jpg」と商品ID.jpgのフォーマットとします。しかし、ユーザーがダウンロードする時には「世界一おいしい? りんご、4/1から販売.jpg」と商品名.jpgにしたいときです。しかし、商品名に含まれる、?(半角疑問符)や/(半角バックスラッシュ)が禁止文字なのです。

手っ取り早いのは、半角の禁止文字を皆全角に置き換えてしまえばよいです。

ということでヘルパーの関数を作成します。


namespace App\Helpers;

class MyHelper
{
    /**
     * 禁止半角文字を全角に
     * @param  string $value
     * @return  string
     */
    public static function convertToValidFilename($value)
    {
        $from = ['\\', '/', ':', '*', '?', '"', '>', '<', '|'];
        $to = ['¥', '/', ':', '*', '?', '”', '>', '<', '|'];

        $patterns = array_map(function($val) { return '#\\'.$val.'#'; }, $from);

        return preg_replace($patterns, $to, $value);
    }
}

preg_replace()の最初の引数に、その前の行のarray_map()で作成された配列を与えているところに注意してください。

通常は、

>>> preg_replace('/123/', 'xyz', '01234');
=> "0xyz4"

のように、パターンには/(半角スラッシュ)で囲んだ文字列を与えますが、配列を与えて複数の置き換えをすべて実行することもできます。

convertToValidFilename()では、$patternsが以下のような配列となり、

Array
(
    [0] => #\\#
    [1] => #\/#
    [2] => #\:#
    [3] => #\*#
    [4] => #\?#
    [5] => #\"#
    [6] => #\>#
    [7] => #\<#
    [8] => #\|#
)

これらは、以下の対応する$toに、

Array
(
    [0] => \
    [1] => /
    [2] => :
    [3] => *
    [4] => ?
    [5] => "
    [6] => >
    [7] => <
    [8] => |
)

に置き換えます。

パターンにおいて、通常使われる/(半角バックスラッシュ)の代わりに、#(半角シャープ)で囲まれているのは、/(半角バックスラッシュ)がパターン内の文字としてありエスケープ文字となってしまうからです。preg_replace()のパターンの境界文字は、そのように必要に応じて変えることができるのです。

早速、tinkerを使用して試してみましょう。

>>> use App\Helpers\MyHelper;
>>> MyHelper::convertToValidFileName('世界一おいしい? りんご、4/1から販売.jpg');
=> "世界一おいしい? りんご、4/1から販売.jpg"

禁止文字が半角から全角に置き換わりましたね。

最後に、これを用いたコントローラのダウンロードメソッドは、こんな感じとなります。

public function download(Product $product)
{
    Storage::download(storage_path().'/'.$product->product-id.'.jpg',     
        MyHelper::convertToValidFileName($product->name).'jpg');
}

By khino