基于 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
{ "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "原始凭证单据(1).doc", "relative_path": "/tmp/2019/12/20/1576810484.4368.436639923.doc", "url": "http://127.0.0.1/pcs-api/storage/tmp/2019/12/20/1576810484.4368.436639923.doc" } ] } }
3、上传扩展名为:.docx 的文件,上传失败,提示:不允许,如图2
{ "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
/** * 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,其值分别为:
application/zip Array ( [0] => zip ) docx
6、分析结果,由于:checkExtensionByMimeType 未明确设置,默认为 true,由 MIME 判定的文件扩展(zip)与给定文件的扩展(docx)不一样,则文件会被认为无效。编辑 Yii 2.0 框架的核心文件:helpers/BaseFileHelper.php,修改方法:getMimeType,以启动调试,依次打印输出:$file、$result,其值分别为:
/** * 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; }
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
<?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)
<?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; } }
docx application/zip xlsx application/zip pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
10、上传模型的验证规则,之前是验证器别名:file,现在调整为:common\components\validators\FileValidator
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']], ]; }
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
{ "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "原始凭证单据(1).docx", "relative_path": "/tmp/2019/12/20/1576825976.9466.2119963527.docx", "url": "http://127.0.0.1/pcs-api/storage/tmp/2019/12/20/1576825976.9466.2119963527.docx" } ] } }
{ "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "周报模板.xlsx", "relative_path": "/tmp/2019/12/20/1576826044.1977.1865556442.xlsx", "url": "http://127.0.0.1/pcs-api/storage/tmp/2019/12/20/1576826044.1977.1865556442.xlsx" } ] } }
{ "code": 10000, "message": "上传资源成功", "data": { "items": [ { "original_file_name": "邮箱使用说明.pptx", "relative_path": "/tmp/2019/12/20/1576826075.8429.300942829.pptx", "url": "http://127.0.0.1/pcs-api/storage/tmp/2019/12/20/1576826075.8429.300942829.pptx" } ] } }
近期评论