お久しぶりです。TOSHIです。
かなりブログ更新サボっていたので久しぶりの投稿です。
最近、WordPress案件の仕事でメディアにxlsx形式のファイルをアップロードした時に、エラーでアップロード出来なかったことがあります。
意外と解決するのに時間がかかったので二度同じことで困らないように備忘録としてブログに記録しておこうと思います。
WordPressにはどんなファイルがアップロード出来る?
全てのファイルをWordPressにアップロード出来るわけではありません。
事前にWordPressにアップロード出来るファイルは決まっています。
基本的には、エクセルファイルやワードなどのoffice系のファイルもアップロード出来ます(WordPressのデフォルト状態でアップロード可能なファイルはバージョンによって異なります)
PSDファイルやAIファイルなど、元々アップロード出来ないファイルも設定やカスタマイズでアップロードを許可することが出来ます。逆に、許可するファイルタイプを制限することも可能です。
メディアからエクセルファイルがアップロード出来ない
このサイトもWordPressで作られているのですが、エクセルファイルはアップロード出来ます。他社さんのサイトでもクーネルワークで作ったWordPressサイトは問題なくアップロード出来たのに、とあるサイトでxlsx形式のエクセルファイルがアップロード出来ず、「このファイルタイプはセキュリティの観点から許可されていません。」というエラーが出てしまいました。
まずはユーザー権限を疑ってみた
セキュリティの観点から許可されていないといっても、サイトはSSL化されている。
むしろされていないサイトでもアップロード出来るサイトはアップロード出来ました。特別セキュリティの問題はなさそうです。
こういうエラーが出る時にユーザー権限系で痛い目を見てきたので、まずは現在ログインしているユーザーの権限を調べてみました。
・・・問題なくファイルのアップロード権限はありました。
そもそも特定のファイルのアップロード権限について、専用の権限があるのかといえばなかったと思います(何かのプラグインで特定のユーザーに対して、制御をかけていたら別ですが)
それに少なくとも「管理者」としてログインしている時には全ての権限を有しているはず。
エラーの発生源を特定してみた
ご丁寧に「このファイルタイプはセキュリティの観点から許可されていません。」というエラーが出ているわけなので、このエラーの発生箇所を特定することにします。
日本語のエラー文をそのまま検索しても探せないので、まずは日本語のエラー文をwordpressの「languages」フォルダにある「admin-ja.po」から検索。英語のエラー文を取得しました。
管理画面上でエラーが出ていたので、この英語のエラー文でwordpressの「wp-admin」ファイルを対象に検索。早速ヒット。
アップロードされたファイルのタイプ、拡張子、必要なユーザー権限の判定をしている箇所らしいのですが、ユーザー権限は問題なかったのでファイルの判定で問題が起きているようです。
ファイルのタイプってそもそも何?
ファイルの拡張子は、「.jpg」「.png」「.gif」など目で見える形になっているものですが、各ファイルは拡張子とは別にファイルタイプというものを持っているようです。
簡単に調べてみましたが、ファイルのタイプは「MIME」と呼ばれていて、WEBサーバーやWEBブラウザがファイルのタイプを認識する為に使われています。
先のエラー箇所でファイルの判定をしている関数のコードを確認して検索すると、「wp-includes」の中のfunctions.phpで定義されていることが分かりました。
MIMEタイプをどのように判定しているのか?
WordPressのアップロード可能なファイルは事前に決められていると最初に書いた通り、アップロード可能なMIMEタイプは決められています。
メディアのアップロードに関しては、アップロード可能なMIMEタイプとアップロードしたファイルのMIMEタイプを比較して、決められたMIMEタイプと一致したら、アップロードが出来る仕組みのようです。
「wp-includes」の中のfunctions.phpにある関数から、WordPressが認識しているMIMEタイプを確認してみました。
※ver4.7.3現在
array( // Image formats. 'jpg|jpeg|jpe' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', 'bmp' => 'image/bmp', 'tiff|tif' => 'image/tiff', 'ico' => 'image/x-icon', // Video formats. 'asf|asx' => 'video/x-ms-asf', 'wmv' => 'video/x-ms-wmv', 'wmx' => 'video/x-ms-wmx', 'wm' => 'video/x-ms-wm', 'avi' => 'video/avi', 'divx' => 'video/divx', 'flv' => 'video/x-flv', 'mov|qt' => 'video/quicktime', 'mpeg|mpg|mpe' => 'video/mpeg', 'mp4|m4v' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm', 'mkv' => 'video/x-matroska', '3gp|3gpp' => 'video/3gpp', // Can also be audio '3g2|3gp2' => 'video/3gpp2', // Can also be audio // Text formats. 'txt|asc|c|cc|h|srt' => 'text/plain', 'csv' => 'text/csv', 'tsv' => 'text/tab-separated-values', 'ics' => 'text/calendar', 'rtx' => 'text/richtext', 'css' => 'text/css', 'htm|html' => 'text/html', 'vtt' => 'text/vtt', 'dfxp' => 'application/ttaf+xml', // Audio formats. 'mp3|m4a|m4b' => 'audio/mpeg', 'ra|ram' => 'audio/x-realaudio', 'wav' => 'audio/wav', 'ogg|oga' => 'audio/ogg', 'mid|midi' => 'audio/midi', 'wma' => 'audio/x-ms-wma', 'wax' => 'audio/x-ms-wax', 'mka' => 'audio/x-matroska', // Misc application formats. 'rtf' => 'application/rtf', 'js' => 'application/javascript', 'pdf' => 'application/pdf', 'swf' => 'application/x-shockwave-flash', 'class' => 'application/java', 'tar' => 'application/x-tar', 'zip' => 'application/zip', 'gz|gzip' => 'application/x-gzip', 'rar' => 'application/rar', '7z' => 'application/x-7z-compressed', 'exe' => 'application/x-msdownload', 'psd' => 'application/octet-stream', 'xcf' => 'application/octet-stream', // MS Office formats. 'doc' => 'application/msword', 'pot|pps|ppt' => 'application/vnd.ms-powerpoint', 'wri' => 'application/vnd.ms-write', 'xla|xls|xlt|xlw' => 'application/vnd.ms-excel', 'mdb' => 'application/vnd.ms-access', 'mpp' => 'application/vnd.ms-project', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12', 'onetoc|onetoc2|onetmp|onepkg' => 'application/onenote', 'oxps' => 'application/oxps', 'xps' => 'application/vnd.ms-xpsdocument', // OpenOffice formats. 'odt' => 'application/vnd.oasis.opendocument.text', 'odp' => 'application/vnd.oasis.opendocument.presentation', 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', 'odg' => 'application/vnd.oasis.opendocument.graphics', 'odc' => 'application/vnd.oasis.opendocument.chart', 'odb' => 'application/vnd.oasis.opendocument.database', 'odf' => 'application/vnd.oasis.opendocument.formula', // WordPerfect formats. 'wp|wpd' => 'application/wordperfect', // iWork formats. 'key' => 'application/vnd.apple.keynote', 'numbers' => 'application/vnd.apple.numbers', 'pages' => 'application/vnd.apple.pages', )
・・・xlsx形式も間違いなく定義されていました。
MIME自体の判定はPHPのバージョンによって異なる
先に結論から言ってしまえば、利用しているサーバーのPHPのバージョンが低く、PHPの中で呼び出される「Fileinfo」というファイル操作のモジュールのバージョンが低かった為、アップロードしたエクセルファイルの正しいMIMEタイプが認識がされていないという状態になっていました。
office2007系の比較的新しいファイルに関して、古いFileinfoは全てzip形式として判定してしまう問題があります。Fileinfoを使ってWordPress(PHP)はアップロードされたファイルのMIMEタイプを判定しているのですが、ここで判定されるMIMEタイプと事前にWordPressが認識していたMIMEタイプの値が異なる為、同じMIMEタイプなのに別の種類のファイルだと認識されてしまいエラーが出ていた、というのが今回の事の真相でした。
解決策とその善し悪し
解決策としては、PHPのバージョンを上げて本当のMIMEタイプを判定する、メディアにアップロードしているファイルのチェックを回避する、全てのアップロードを許可してそもそもチェックしないなどが考えられます。
しかし、アップロードに困っているからといって、下記のコードで全てのファイルのアップロードを許可したり、、、
//wp-config.php に下記コードを書くと全てのファイルがアップロード出来てしまう。。 define( ‘ALLOW_UNFILTERED_UPLOADS’, true );
全てのMIMEタイプの判定をしないようにするプラグインで回避するのはセキュリティを考慮するとオススメは出来ません。
(WordPressがタイプを判定して制限をしているのにもそれ相応の理由があります)
今回に関しては、クライアントワークでPHPのバージョンを上げるのもリスクであった為、特定のエクセルファイルのみMIMEタイプの判定を回避し、アップロード出来るようにしました。
add_filter('wp_check_filetype_and_ext', 'my_filter_wp_check_filetype_and_ext', 10, 4); function my_filter_wp_check_filetype_and_ext($args, $file, $filename, $mimes){ //省略... $wp_filetype = wp_check_filetype( $filename, $mimes ); $ext = $wp_filetype['ext']; $type = $wp_filetype['type']; $proper_filename = false; //省略... $check_xls_exts = array('xlsx'); if( !in_array($ext, $check_xls_exts, true) ) return $args; //省略... $finfo = finfo_open( FILEINFO_MIME_TYPE ); $real_mime = finfo_file( $finfo, $file ); finfo_close( $finfo ); if( 'application/zip' === $real_mime ){ $args = compact( 'ext', 'type', 'proper_filename' ); } return $args; }
現在の拡張子が「xlsx」で、MIMEタイプが「application/zip」(古いFileinfoの判定ではzipだと判定されているようです)と判定されているもののみアップロード可能に出来ました。
WordPressはPHPの上で動いている
WordPressが持つ関数が便利なので時々忘れそうになってしまいますが、WordPressはあくまでPHPの上で動いているということを再確認しました。PHPのバージョン、利用されているモジュール関係は問題の原因になりやすいのでこれからも注意が必要ですね。
FileinfoのバージョンによるMIMEの違いは、下記のサイトを参考にさせて頂きました。