Help us understand the problem. What is going on with this article?

Laravel で作ったシステムに Excel をアップロードしたら拡張子が zip になった

環境

環境は ローカルのHomestead本番のVPS の2つで、どちらでも発生。

  • Laravel Framework 5.8.24
  • PHP 7.3.4 (PHP 7.2.16)
  • Ubuntu 18.04.2 LTS (CentOS Linux release 7.6.1810)
  • Homestead (ConoHa VPS Laravelイメージ)

何がどうなった?

エクセルをアップロードし、 $file->guessExtension() で拡張子を取得したところ zip となってしまった。
さらに $file->getMimeType() をみたところ application/zip となっていた。

クライアントの情報を取得する関数を使うと、それぞれ $file->guessClientExtension()
xlsx となり、 $file->getClientMimeType()application/vnd.openxmlformats-officedocument.spreadsheetml.sheet となった。
表にまとめると以下。

extension mime_type
larval zip application/zip
client xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

エクセルの mime_type なっが。

どうして?

finfo がうまく判定できないらしい。
ライブラリをガンガン追って行ったところ、Laravel の問題というよりは、内部で使用している Symfony のクラスの内部で使用している finfo に行きついた。
実際に finfo で mime_type を判定させてみると、 application/zip となってしまった。
application/zip から拡張子を推測すると zip になるという流れである。
StackOverflow の関連記事
上記記事を読むと、ほかの Microsoft の Office 系ファイルもダメらしい。

どうする?

解決策はいくつかあると思う。

  • Symfony の該当クラスは複数の guesser を扱えるので、Laravel で頑張って拡張する
  • finfo を修正する
  • 素直にクライアントの拡張子を利用する

などなど。

自分は 上記のどれも採用せず、拡張子を推測するための関数をつくった(理由は後述)。
作ったと言っても、Laravel/Symfonyが用意したものと、先人の知恵を組み合わせただけだが。

基本方針

  • 拡張子に関してはクライアント(ファイルアップロード者)を信じるという選択は取りたくない
  • Laravel を拡張するのはしんどそう
  • finfo を修正するのは移植性がない
  • 不具合報告があったのはとりあえず excel だけ

参考ソース

public function guessExtension(UploadedFile $file): string
{
    $extension = $file->guessExtension();
    if ($extension !== 'zip') {
        return $extension;
    }

    $guessedMimeType = $file->getMimeType();
    $clientMimeType = $file->getClientMimeType();
    if ($guessedMimeType === $clientMimeType) {
        return $extension;
    }

    $map = [
        'application/vnd.ms-word.document.macroEnabled.12' => 'docm',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',

        'application/vnd.ms-powerpoint.template.macroEnabled.12' => 'potm',
        'application/vnd.ms-powerpoint.addin.macroEnabled.12' => 'ppam',
        'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => 'ppsm',
        'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => 'pptm',
        'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
        'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',

        'application/vnd.ms-excel.template.macroEnabled.12' => 'xltm',
        'application/vnd.ms-excel.addin.macroEnabled.12' => 'xlam',
        'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => 'xlsb',
        'application/vnd.ms-excel.sheet.macroEnabled.12' => 'xlsm',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
    ];

    return $map[$clientMimeType] ?? $extension;
}

作った後に「 Symfony の同様の力技を行使する guesser を使う手もあったな」などと思ったけど、いろんな mime_type を検討するのはしんどいので、とりあえずこれで。

shimon_haga
フルスタックWebエンジニア。 インフラからフロントエンドまで。 Webシステムやアプリで使う知識と経験は一通り。 PHP/JavaScriptなどが主戦場。 最近は React(Native), Vue, Laravel あたりをふらふらと。
https://shimon.biz
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした