基于 Yii 2.0 上传文件时,可接受上传的文件扩展名列表中已经添加:.docx,仍然不允许上传的解决
1、可接受上传的文件扩展名列表:ogg, pdf, xml, zip, gz, mp4, mp3, wav, webm, gif, jpeg, jpg, png, webp, svg, svgz, tiff, css, csv, txt, vcf, vcard, mov, qt, mkv, mk3d, mka, mks, wmv, flv, doc
2、上传扩展名为:.doc 的文件,上传成功,如图1
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "原始凭证单据(1).doc", "relative_path": "/tmp/2019/12/20/1576810484.4368.436639923.doc", } ] } } |
3、上传扩展名为:.docx 的文件,上传失败,提示:不允许,如图2
1 2 3 4 | { "code": 226004, "message": "数据验证失败:只允许使用以下文件扩展名的文件:ogg, pdf, xml, zip, gz, mp4, mp3, wav, webm, gif, jpeg, jpg, png, webp, svg, svgz, tiff, css, csv, txt, vcf, vcard, mov, qt, mkv, mk3d, mka, mks, wmv, flv, doc, docx。" } |
4、编辑 Yii 2.0 框架的核心文件:validators/FileValidator.php,修改方法:validateExtension($file),以启动调试,再次请求,输出:2,如图3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /** * Checks if given uploaded file have correct type (extension) according current validator settings. * @param UploadedFile $file * @return bool */ protected function validateExtension( $file ) { $extension = mb_strtolower( $file ->extension, 'UTF-8' ); if ( $this ->checkExtensionByMimeType) { $mimeType = FileHelper::getMimeType( $file ->tempName, null, false); if ( $mimeType === null) { echo 1; exit ; return false; } $extensionsByMimeType = FileHelper::getExtensionsByMimeType( $mimeType ); if (!in_array( $extension , $extensionsByMimeType , true)) { echo 2; exit ; return false; } } if (!in_array( $extension , $this ->extensions, true)) { echo 3; exit ; return false; } return true; } |
5、checkExtensionByMimeType:是否通过文件的 MIME 类型来判断其文件扩展。若由 MIME 判定的文件扩展与给定文件的扩展不一样,则文件会被认为无效。默认为 true,代表执行上述检测。依次打印输出:$mimeType、$extensionsByMimeType、$extension,其值分别为:
1 2 3 4 5 6 7 8 | application/zip Array ( [0] => zip ) docx |
6、分析结果,由于:checkExtensionByMimeType 未明确设置,默认为 true,由 MIME 判定的文件扩展(zip)与给定文件的扩展(docx)不一样,则文件会被认为无效。编辑 Yii 2.0 框架的核心文件:helpers/BaseFileHelper.php,修改方法:getMimeType,以启动调试,依次打印输出:$file、$result,其值分别为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** * Determines the MIME type of the specified file. * This method will first try to determine the MIME type based on * [finfo_open](http://php.net/manual/en/function.finfo-open.php). If the `fileinfo` extension is not installed, * it will fall back to [[getMimeTypeByExtension()]] when `$checkExtension` is true. * @param string $file the file name. * @param string $magicFile name of the optional magic database file (or alias), usually something like `/path/to/magic.mime`. * This will be passed as the second parameter to [finfo_open()](http://php.net/manual/en/function.finfo-open.php) * when the `fileinfo` extension is installed. If the MIME type is being determined based via [[getMimeTypeByExtension()]] * and this is null, it will use the file specified by [[mimeMagicFile]]. * @param bool $checkExtension whether to use the file extension to determine the MIME type in case * `finfo_open()` cannot determine it. * @return string the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined. * @throws InvalidConfigException when the `fileinfo` PHP extension is not installed and `$checkExtension` is `false`. */ public static function getMimeType( $file , $magicFile = null, $checkExtension = true) { if ( $magicFile !== null) { $magicFile = Yii::getAlias( $magicFile ); } if (! extension_loaded ( 'fileinfo' )) { if ( $checkExtension ) { return static ::getMimeTypeByExtension( $file , $magicFile ); } throw new InvalidConfigException( 'The fileinfo PHP extension is not installed.' ); } $info = finfo_open(FILEINFO_MIME_TYPE, $magicFile ); if ( $info ) { $result = finfo_file( $info , $file ); finfo_close( $info ); if ( $result !== false) { print_r( $file ); echo "\n" ; echo "\n" ; print_r( $result ); exit ; return $result ; } } return $checkExtension ? static ::getMimeTypeByExtension( $file , $magicFile ) : null; } |
1 2 3 | E:\phpuploadtmp\phpF3BE.tmp application/zip |
7、PHP 7.2 语言本身的 Fileinfo 函数:finfo_file,对于 .docx 的文件无法准确识别(感觉上),实则 .docx 是一种压缩的 xml 格式,这就是为什么 file_info() 返回 application_zip 的原因(完全正确)。新建文件:finfo_file.php,输出:application/zip,如图4
1 2 3 4 5 6 7 8 9 10 11 12 | <?php $info = finfo_open(FILEINFO_MIME_TYPE, null); if ( $info ) { $result = finfo_file( $info , 'D:\华栖云\原始凭证单据(1).docx' ); finfo_close( $info ); if ( $result !== false) { echo $result ; } } ?> |
8、参考官方文档:https://www.php.net/manual/zh/function.finfo-file.php ,其中存在 MS Office 2007 扩展(pptx,xlsx,docx) 没有默认的 Mime 类型,它们具有 application/zip Mime 类型的解决方案,本质上,做了一下特殊处理,以安全地防范假扩展。如图5
9、新建类文件:common/logics/Upload.php,继承至 \yii\validators\FileValidator,以覆写验证扩展名的方法:validateExtension($file)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/12/20 * Time: 14:28 */ namespace common\components\validators; use yii\base\InvalidConfigException; use yii\helpers\FileHelper; use yii\web\UploadedFile; /** * FileValidator verifies if an attribute is receiving a valid uploaded file. * * Note that you should enable `fileinfo` PHP extension. * * @property int $sizeLimit The size limit for uploaded files. This property is read-only. * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class FileValidator extends \yii\validators\FileValidator { /** * Checks if given uploaded file have correct type (extension) according current validator settings. * @param UploadedFile $file * @return bool * @throws InvalidConfigException when the `fileinfo` PHP extension is not installed and `$checkExtension` is `false`. */ protected function validateExtension( $file ) { $extension = mb_strtolower( $file ->extension, 'UTF-8' ); if ( $this ->checkExtensionByMimeType) { $mimeType = FileHelper::getMimeType( $file ->tempName, null, false); if ( $mimeType === null) { return false; } $extensionsByMimeType = FileHelper::getExtensionsByMimeType( $mimeType ); if (!in_array( $extension , $extensionsByMimeType , true)) { // MS Office 2007 扩展(docx、xlsx),其 MIME 类型为 application/zip 的特殊处理 $msMimeTypes = [ 'application/zip' ]; $msExtensions = [ 'docx' , 'xlsx' ]; if (!(in_array( $mimeType , $msMimeTypes ) && in_array( $extension , $msExtensions ))) { return false; } } } if (!in_array( $extension , $this ->extensions, true)) { return false; } return true; } } |
1 2 3 4 5 6 7 8 | docx application/zip xlsx application/zip pptx application/vnd.openxmlformats-officedocument.presentationml.presentation |
10、上传模型的验证规则,之前是验证器别名:file,现在调整为:common\components\validators\FileValidator
1 2 3 4 5 6 | public function rules() { return [ [[ 'files' ], 'file' , 'skipOnEmpty' => false, 'extensions' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'extensions' ], 'mimeTypes' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'mimeTypes' ], 'minSize' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'minSize' ], 'maxSize' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'maxSize' ], 'maxFiles' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'maxFiles' ]], ]; } |
1 2 3 4 5 6 | public function rules() { return [ [[ 'files' ], 'common\components\validators\FileValidator' , 'skipOnEmpty' => false, 'extensions' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'extensions' ], 'mimeTypes' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'mimeTypes' ], 'minSize' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'minSize' ], 'maxSize' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'maxSize' ], 'maxFiles' => Yii:: $app ->params[ 'pcsApi' ][ 'asset' ][ 'upload' ][ 'maxFiles' ]], ]; } |
11、可接受上传的文件扩展名列表:ogg, pdf, xml, zip, gz, mp4, mp3, wav, webm, gif, jpeg, jpg, png, webp, svg, svgz, tiff, css, csv, txt, vcf, vcard, mov, qt, mkv, mk3d, mka, mks, wmv, flv, doc, docx, xls, xlsx, ppt, pptx。可接受上传的 MIME 类型列表:application/ogg, application/pdf, application/xml, application/zip, application/gzip, audio/mp4, audio/mpeg, audio/ogg, audio/vnd.wave, audio/webm, image/gif, image/jpeg, image/png, image/webp, image/svg+xml, image/tiff, text/css, text/csv, text/plain, text/vcard, text/xml, video/mpeg, video/mp4, video/ogg, video/quicktime, video/webm, video/x-matroska, video/x-ms-wmv, video/x-flv, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation。分别上传:docx, xlsx, pptx,皆上传成功,符合预期,如图6
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "原始凭证单据(1).docx", "relative_path": "/tmp/2019/12/20/1576825976.9466.2119963527.docx", } ] } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "周报模板.xlsx", "relative_path": "/tmp/2019/12/20/1576826044.1977.1865556442.xlsx", } ] } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "邮箱使用说明.pptx", "relative_path": "/tmp/2019/12/20/1576826075.8429.300942829.pptx", } ] } } |
近期评论