0
2

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 3 years have passed since last update.

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

Last updated at Posted at 2020-07-13

環境

環境は ローカルの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 の関連記事] (https://stackoverflow.com/questions/6595183/docx-file-type-in-php-finfo-file-is-application-zip)
上記記事を読むと、ほかの 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 を検討するのはしんどいので、とりあえずこれで。

0
2
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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?