写点什么

Yii2 文件 / 图片上传实例

作者:Owen Zhang
  • 2022 年 5 月 26 日
  • 本文字数:6364 字

    阅读完需:约 21 分钟

Yii2文件/图片上传实例

本文环境 Windows10,PHP7.1,Nginx1.8,Yii 2.0

不懂的可以评论或联系我邮箱:owen@owenzhang.com

著作权归 OwenZhang 所有。商业转载请联系 OwenZhang 获得授权,非商业转载请注明出处。


Yii2 框架介绍

Yii 是一个高性能,基于组件的 PHP 框架,用于快速开发现代 Web 应用程序。 名字 Yii (读作 )在中文里有“极致简单与不断演变”两重含义, 也可看作 Yes It Is! 的缩写。


Yii 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。 因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用, 如门户网站、社区、内容管理系统(CMS)、 电子商务项目和 RESTful Web 服务等。

文件上传父类

类函数说明

  • 根据时间创建目录 createDir

  • 获取 URL 路径 getUrlPath

  • 获取文件名 getFileName

  • 获取文件大小 getFileSize

  • 获取文件类型 getFileType

  • 获取文件的 Mine 类型 getFileMime

  • 获取文件 md5 getFileMd5

  • 获取图片的宽度 getThumbWidth

  • 获取图片的高度 getThumbHeight

  • 获取文件保存 save

具体类代码

<?php
namespace common\helpers;
use yii;use yii\base\Model;use yii\base\Object;use yii\web\UploadedFile;use yii\helpers\FileHelper;use Exception;
class UploadHelper extends Object{ // 处理的model public $model;
// 最大允许上传的文件大小 5Mb public $maxSize = 5227520;
// 上传文件表单名称 public $fileInputName = 'file';
// 图片保存绝对路径 public $savePath;
// 文件访问路径前缀 public $urlPathPrefix;
// 文件后缀格式 public $extensions;
// 文件Mime 类型 public $mimeTypes;
// 上传文件处理类 private $uploadFile;
// 文件保存路径 private $filePath;
// 文件访问路径 private $urlPath;
// 文件大小 private $fileSize;
// 文件Mime类型 private $fileMime;
// 文件后缀 private $fileExtension;
// 文件名 private $fileName;
// 图片宽度 private $thumbWidth;
// 图片高度 private $thumbHeight;
// 文件MD5哈希值 private $fileMd5;
public function init() { if (empty($this->model) || !($this->model instanceof Model)) { throw new Exception(Yii::t('app', 'No delivery file class passed')); }
// 上传文件接收 $strField = $this->fileInputName; $this->model->$strField = $this->uploadFile = UploadedFile::getInstanceByName($this->fileInputName);
if (empty($this->uploadFile)) { throw new Exception(Yii::t('app', 'No file uploaded')); }
// 获取上传文件的后缀格式 $extension = $this->uploadFile->extension; $fileMime = $this->uploadFile->type; $thumbExtensionArray = ['jpg', 'jpeg', 'gif', 'png', 'bmp']; $this->thumbWidth = $this->thumbHeight = 0;
// 判断上传的文件是否是图片文件 if(in_array($extension, $thumbExtensionArray)){ $this->savePath = Yii::getAlias('@frontend/web/uploads/material/images'); $this->urlPathPrefix = '/uploads/material/images'; $this->extensions = $thumbExtensionArray; $this->mimeTypes = ['image/jpeg', 'image/bmp', 'image/gif', 'image/png', 'image/pjpeg', 'image/x-png']; $thumbInfo = getimagesize($this->uploadFile->tempName); $this->thumbWidth = isset($thumbInfo[0]) ? $thumbInfo[0] : $this->thumbWidth; $this->thumbHeight = isset($thumbInfo[1]) ? $thumbInfo[1] : $this->thumbHeight; } else { $this->savePath = Yii::getAlias('@frontend/web/uploads/material/files'); $this->urlPathPrefix = '/uploads/material/files'; $this->extensions = ['txt', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf']; $this->mimeTypes = ['text/plain', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/pdf']; }
// 验证文件类型是否在允许的范围 if(!in_array($extension, $this->extensions) || !in_array($fileMime, $this->mimeTypes)){ throw new Exception(Yii::t('app', 'This file type does not allow uploading')); }
// 目录不存在创建 $this->savePath = rtrim($this->savePath, '/'); if (!file_exists($this->savePath) && !FileHelper::createDirectory($this->savePath)) { throw new Exception(Yii::t('app', 'No permission to create') . $this->savePath); }
// 限制上传的文件小于5M if($this->uploadFile->size > $this->maxSize){ throw new Exception(Yii::t('app', 'Uploaded files cannot exceed') . ($this->maxSize / 1048576) . 'Mb'); }
// 上传文件验证 if (!$this->model->validate()) { throw new Exception($this->model->getFirstError($strField)); }
list($path, $datePath) = $this->createDir($this->savePath);
// 文件名命名 $fileName = time() . mt_rand(1000, 9999);
$this->filePath = $path . $fileName . '.' . $this->uploadFile->getExtension(); $this->urlPath = rtrim($this->urlPathPrefix, '/') . '/' . $datePath . '/' . $fileName . '.' . $this->uploadFile->getExtension(); $this->fileExtension = $extension; $this->fileSize = round($this->uploadFile->size / 1024, 2) . ' Kb'; $this->fileMime = $this->uploadFile->type; $this->fileName = $fileName; $this->fileMd5 = hash_file('md5', $this->uploadFile->tempName);
}
/** * 根据时间创建目录 */ public function createDir($path) { $datePath = date('Y') . '/' . date('md'); $path = $path . '/' . $datePath . '/'; if (!file_exists($path)) { FileHelper::createDirectory($path); }
return [$path, $datePath]; }
/** * 获取URL路径 * @return mixed */ public function getUrlPath() { return $this->urlPath; }
/** * 获取文件名 * @return mixed */ public function getFileName() { return $this->fileName; }
/** * 获取文件大小 * @return mixed */ public function getFileSize() { return $this->fileSize; }
/** * 获取文件类型 * @return mixed */ public function getFileType() { return $this->fileExtension; }
/** * 获取文件的Mine类型 * @return mixed */ public function getFileMime() { return $this->fileMime; }
public function getFileMd5() { return $this->fileMd5; }
/** * 获取图片的宽度 * @return mixed */ public function getThumbWidth() { return $this->thumbWidth; }
/** * 获取图片的高度 * @return mixed */ public function getThumbHeight() { return $this->thumbHeight; }
public function save() { return $this->uploadFile->saveAs($this->filePath); }}
复制代码

文件上传的漏洞和防御

漏洞描述

没有做文件的限制,导致用户上传了非法的文件,或者过大的文件导致服务器过载。用户上传木马文件等


例如以下就是木马代码


<?php eval($_GET['cmd']);?>
复制代码

漏洞防御

  1. 审查用户上传的文件时加入了“Content-Type”验证

  2. “Content-Type”是 image/jpeg 或者 image/png 时文件可以上传 成功

  3. 文件上传验证类

  4. 验证会话身份,用于防止 csrf 攻击

  5. 添加白名单的来限制上传的文件后缀和上传的来源

  6. 文件大小的限制

  7. 用户上传的源文件删除

  8. 上传过程中产生的临时文件删除

  9. imagecreatefromjpeg()和 imagecreatefrompng()来过来文件的有害元数据

  10. 上传接口的数据校验

  11. 现在更多的是上传到 OSS 云存储上

文件上传验证类

基于安全方面的考虑,您应当增加有关允许哪些用户上传文件的限制和验证。

类函数说明

  • 验证规则 rules

  • file 为文件上传的参数名 public $file;

  • image 为图片上传的参数名 public $image;

  • 文件上传的文件类型 'extensions' => ['jpg', 'png', 'gif', 'txt']

  • 增加了对文件上传的限制。只能上传 .gif、.txt、.jpg、.png 文件,文件大小必须小于 50000 kB:

  • 要不要验证 mimeTypes 类型 'checkExtensionByMimeType' => false,

  • 文件的 Mine 类型 'mimeTypes' => ['image/jpeg', 'image/bmp', 'image/gif', 'image/png', 'image/pjpeg', 'image/x-png', 'text/plain'],

  • 验证场景 scenarios

  • 验证属性标签 attributeLabels

具体类代码

<?php
namespace common\models;
use yii;use yii\base\Model;
class UploadForm extends Model{ public $file;
public $image;
/** * {@inheritdoc} */ public function rules() { return [ [ ['file'], 'file', 'extensions' => ['jpg', 'png', 'gif', 'txt'], 'mimeTypes' => ['image/jpeg', 'image/bmp', 'image/gif', 'image/png', 'image/pjpeg', 'image/x-png', 'text/plain'], 'checkExtensionByMimeType' => false, // 要不要验证 mimeTypes 类型 'on' => 'file', ], [ ['image'], 'image', 'extensions' => ['jpg', 'png', 'gif', 'bmp'], 'mimeTypes' => ['image/jpeg', 'image/bmp', 'image/gif', 'image/png', 'image/pjpeg', 'image/x-png', 'image/bmp'], 'checkExtensionByMimeType' => false, // 要不要验证 mimeTypes 类型 'on' => 'image', ] ]; }
/** * {@inheritdoc} */ public function scenarios() { return [ 'image' => ['image'], 'file' => ['file'], ]; }
/** * {@inheritdoc} */ public function attributeLabels() { return [ 'file' => Yii::t('app', 'File'), 'image' => Yii::t('app', 'Image'), ]; }}
复制代码

文件上传实例接口方法

类函数说明

  • 文件上传 actionFileUpload

  • 图片上传实例方法 actionImageUpload

  • 初始化文件上传类 $model = New UploadHelper(['fileInputName' => 'file','model' => new UploadForm(['scenario' => 'file']),]);

  • 上传成功后返回结果 return $this->asJson

  • 在服务器的 PHP 临时文件夹中创建了一个被上传文件的临时副本。这个临时的副本文件会在脚本结束时消失。要保存被上传的文件,我们需要把它拷贝到另外的位置。

  • if (!$model->save()) {throw new Exception(Yii::t('app', 'File upload failed'));}代码检测了文件是否已存在,如果不存在,则把文件拷贝到名为 "upload" 的目录下。

  • 最终上传成功后 会销毁临时文件

文件上传实例方法

/**     * 文件上传     * @return yii\web\Response     */    public function actionFileUpload()    {        try {            $model = New UploadHelper([                'fileInputName' => 'file',                'model' => new UploadForm(['scenario' => 'file']),            ]);
if (!$model->save()) { throw new Exception(Yii::t('app', 'File upload failed')); }
return $this->asJson([ 'status' => 'ok', 'message' => Yii::t('app', 'Uploaded successfully and saved in the attachment library'), 'data' => [ 'file_url' => $model->getUrlPath(), 'file_name' => $model->getFileName(), 'file_type' => $model->getFileType(), 'file_size' => $model->getFileSize(), 'file_mime' => $model->getFileMime(), 'file_md5' => $model->getFileMd5(), 'thumb_width' => $model->getThumbWidth(), 'thumb_height' => $model->getThumbHeight(), ] ]); } catch (Exception $e) { return $this->asJson([ 'status' => 'error', 'message' => $e->getMessage() ]); } }
复制代码

图片上传实例方法

/**     * 图片上传     * @return yii\web\Response     */    public function actionImageUpload()    {        try {            $model = New UploadHelper([                'fileInputName' => 'image',                'model' => new UploadForm(['scenario' => 'image']),            ]);
if (!$model->save()) { throw new Exception(Yii::t('app', 'File upload failed')); }
return $this->asJson([ 'status' => 'ok', 'message' => Yii::t('app', 'Uploaded successfully and saved in the attachment library'), 'data' => [ 'file_url' => $model->getUrlPath(), 'file_name' => $model->getFileName(), 'file_type' => $model->getFileType(), 'file_size' => $model->getFileSize(), 'file_mime' => $model->getFileMime(), 'file_md5' => $model->getFileMd5(), 'thumb_width' => $model->getThumbWidth(), 'thumb_height' => $model->getThumbHeight(), ] ]); } catch (Exception $e) { return $this->asJson([ 'status' => 'error', 'message' => $e->getMessage() ]); } }
复制代码


Buy me a cup of coffee :)

觉得对你有帮助,就给我打赏吧,谢谢!


Buy me a cup of coffee :)


https://www.owenzhang.com/wechat_reward.png

发布于: 刚刚阅读数: 3
用户头像

Owen Zhang

关注

还未添加个人签名 2020.05.10 加入

I actually hate programming, but I love solving problems. Phper & Gopher. https://gitee.com/owenzhang24 Email: owen@owenzhang.com

评论

发布
暂无评论
Yii2文件/图片上传实例_php_Owen Zhang_InfoQ写作社区