8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravelからアップロードしてs3に格納した画像が直アクセスするとダウンロードされる

Last updated at Posted at 2017-03-15

Laravelアプリのファイルアップロードからアップした画像ファイルの配信サーバーとしてAWSのS3を利用する場合、いくつか留意事項がある。

s3バケット以下へのアクセス許可

デフォルトだと、バケット以下のファイルはpublicではないので、バケットのプロパティ設定の「アクセス許可」「バケットポリシーの編集」を設定する

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::[バケット名]/*"
        }
    ]
}

格納画像ファイルのMimeType指定

Laravel側でアップロードした画像をS3に格納するには、config/filesystems.phpでs3の設定をした上で、Controllerでこんな感じ。

public function store($request)
{

    ...

    if ($request->hasFile('photo'))
        $stored_path = $request->photo->store('photos', 's3');

    ...

}

s3で公開されるURLは、Storage::url($stored_path)で得られる。

HTMLのimgタグのsrcに指定すると問題なく表示されるが、そのURLにブラウザから直接アクセスすると、MimeTypeが指定されていないので、デフォルトの「octet-stream」扱いになって、ダウンロードされてしまう。

これを回避するには同じくS3側のファイルのプロパティから、メタデータとして適切なMimeType設定をすればいいが、S3へのファイル保存時にアプリでやるには、Laravel5.4〜の場合、上のstoreメソッドをこのように書く。


$options = [
    'visibility' => 'public',
    'mimetype'   => 'image/jpeg'
];
if ($request->hasFile('photo'))
    $stored_path = $request->photo->store('photos', 's3', $options);

storeメソッドの第3引数が、filesystem Adapter(この場合s3)へ渡すオプションとなる。ちなみに visibilityは上で書いた公開設定をpublicにするという設定。

(参考)
https://laravel.com/api/5.4/Illuminate/Http/UploadedFile.html#method_store

5.3だとこれができないので困る。てか、困った方々による議論の末に、上のstoreオプションが5.4に取り込まれたよう。その経緯はこちら。

(参考)
https://github.com/laravel/framework/pull/16416

5.3で同じことをするには、UploadedFileのstoreメソッドと、そこから呼ばれるfilesystemのAdapterクラスの書き込みメソッドを自前でoverrideするような形になる。


public function store($requset)
{
    ...

    if ($request->hasFile('photo'))
        $stored_path = $this->_storeUploadFile($request->photo);

    ...

}

private function _storeUploadFile($uploadFile)
{
    $driver = Storage::disk('s3')->getDriver();

    $mimeType = $uploadFile->getClientMimeType();
    $stream = fopen($uploadFile->getRealPath(), 'r+');
    $path = trim('photos/'. $uploadFile->hashName(), '/');
    $result = $driver->putStream($path, $stream, [
            'visibility' => 'public',
            'mimetype'   => $mimeType
        ]);
    if (is_resource($stream)) fclose($stream);
    return $result ? $path : false;

}

MimeTypeの判定は UploadFileの親クラスがgetClientMimeTypeというメソッドを持ってるようなんでそれを利用。精度は知らない。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?