Yii2文件/图片上传实例

2022-05-26 11:09:53 浏览数 (2)

本文环境 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复制
<?php

namespace commonhelpers;

use yii;
use yiibaseModel;
use yiibaseObject;
use yiiwebUploadedFile;
use yiihelpersFileHelper;
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);
    }
}

文件上传的漏洞和防御

漏洞描述

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

例如以下就是木马代码

代码语言:txt复制
<?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复制
<?php

namespace commonmodels;

use yii;
use yiibaseModel;

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" 的目录下。
  • 最终上传成功后 会销毁临时文件

文件上传实例方法

代码语言:php复制
/**
     * 文件上传
     * @return yiiwebResponse
     */
    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()
            ]);
        }
    }

图片上传实例方法

代码语言:php复制
/**
     * 图片上传
     * @return yiiwebResponse
     */
    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()
            ]);
        }
    }

0 人点赞