PHP文件上传类完整代码

最近在用PHP写一个后台项目,需要原生PHP做一个“PHP文件上传”功能。当然了,最主要的就是“PHP文件上传图片”。

下面是我整理了一份完整的代码,包括前端和后端,如下:

 

一、前端代码

 

index.php

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>文件上传</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" value=""><br>
    <input type="submit" value="提交"/><br>
</form>
</body>
</html>

 

二、PHP代码

主要是放PHP一些逻辑处理代码。

 

upload.php

<?php
/**
 * Created by PhpStorm.
 * User: fujieace.com
 */

include_once "./UploadFile.class.php";

$upload = new UploadFile(); 
if ($upload->uploadFile("file")) { // file就是input框中的name属性值
    echo '图片上传成功';
}else{
    echo $upload->errorInfo;
}

 

三、PHP文件上传类库代码

 

UploadFile.class.php

<?php
/**
 * Created by PhpStorm.
 * User: fujieace.com
 */

class UploadFile
{
    //文件上传路径
    protected $path = '../public/uploads/images/';

    //允许文件上传的后缀
    protected $allowSuffix = ['jpg', 'jpeg','png','gif']; // 'wbmp'

    //mime类型
    protected $allowMime = ['image/jpg', 'image/jpeg', 'image/png','image/gif'];// 'image/wbmp'

    //允许上传的大小
    protected $maxSize = 2 * 1024 * 1024;//1024*1024是1M,5 * 1024 * 1024 是5M

    //是否启用默认的前缀
    protected $isRandName = true;

    //文件的前缀
    protected $prefix = '453535_';

    //错误号和错误信息
    protected $errorNumber;
    protected $errorInfo;

    //文件的信息

    //文件名
    protected $oldName;

    //文件的后缀
    protected $suffix;

    //文件的大小
    protected $size;

    //文件的mime
    protected $mime;

    //文件的临时文件的路径
    protected $tmpName;

    //文件新名字
    protected $newName;

    /**
     * 构造方法
     * Upload constructor.
     * @param array $arr
     */
    public function __construct($arr = [])
    {
        foreach ($arr as $key => $value) {
            $this->setOption($key, $value);
        }
    }

    /**
     * 判断$key是不是我的成员属性,如果是就设置
     * @param $key
     * @param $value
     */
    protected function setOption($key, $value)
    {
        //得到所有的成员属性
        $keys = array_keys(get_class_vars(__CLASS__));
        if (in_array($key, $keys)) {
            $this->$key = $value;
        }
    }

    /**
     * 文件上传函数
     * key 就是input框中的name属性值
     * @param $key
     * @return bool|string
     */
    public function uploadFile($key)
    {
        $this->path = $this->path.date('Ym',time()).'/';//路径变成 ../public/uploads/images/202007/

        //判断有没有设置路径 path
        if (empty( $this->path)) {
            $this->setOption('errorNumber', -1);
            return false;
        }
        //判断该路径是否存在是否可写
        if (!$this->check()) {
            $this->setOption('errorNumber', -2);
            return false;
        }
        //判断$_FILES里面的error信息是否为0,如果为0则说明文件信息在服务器端可以直接获取,提取信息保存到成员属性中
        $error = $_FILES[$key]['error'];
        if ($error) {
            $this->setOption('errorNumber', -3);
            return false;
        } else {
            //提取文件相关信息并且保存到成员属性中
            $this->getFileInfo($key);
        }
        //判断文件的大小、mime、后缀是否符合
        if (!$this->checkSize() || !$this->checkMime() || !$this->checkSuffix()) {
            return false;
        }

        //判断图片头信息类型是否符合
        if (!$this->picHeaderType($this->tmpName)){
            $this->setOption('errorNumber', -41);
            return false;
        }

        //判断图片是否包含非法数据,例如:木马 后门
        if(!$this->CheckIllegal($this->tmpName)){
            $this->setOption('errorNumber', -42);
            return false;
        }

        //得到新的文件名字
        $this->newName = $this->createNewName();
        //判断是否是上传文件,并且是移动上传文件
        if (is_uploaded_file($this->tmpName)) {
            if (move_uploaded_file($this->tmpName,  $this->path . $this->newName)) {
                return  $this->path . $this->newName;
            } else {
                $this->setOption('errorNumber', -7);
                return false;
            }
        } else {
            $this->setOption('errorNumber', -6);
            return false;
        }
    }

    /**
     * 检测文件夹是否存在,是否可写
     * @return bool
     */
    protected function check()
    {

       //文件夹不存在或者不是目录。创建文件夹
        if (!file_exists($this->path) || !is_dir($this->path)) {
            return mkdir($this->path, 0777, true);
        }
        //判断文件是否可写
        if (!is_writeable($this->path)) {
            return chmod($this->path, 0777);
        }
        return true;
    }

    /**
     * 根据key得到文件信息
     * @param $key
     */
    protected function getFileInfo($key)
    {
        //得到文件的名字
        $this->oldName = $_FILES[$key]['name'];
        //得到文件的mime类型
        $this->mime = $_FILES[$key]['type'];
        //得到文件的临时文件
        $this->tmpName = $_FILES[$key]['tmp_name'];
        //得到文件大小
        $this->size = $_FILES[$key]['size'];
        //得到文件后缀
        $this->suffix = pathinfo($this->oldName)['extension'];
    }

    /**
     * 通过头信息判断图片类型
     * @return bool
     */
    protected function picHeaderType ( $file ){
        $array = array(
            array("FFD8FFE1","jpg"),
            array("89504E47","png"),
            array("47494638","gif"),
            array("FFD8FFE000104A46", "jpeg")
            //      array("49492A00","tif"),
            ////      array("424D","bmp"),
            /// //      array("41433130","dwg"),
            /// //      array("38425053","psd"),
            /// //         array("7B5C727466","rtf"),
            /// //         array("3C3F786D6C","xml"),
            /// //         array("68746D6C3E","html"),
            /// //         array("44656C69766572792D646174","eml"),
            /// //         array("CFAD12FEC5FD746F","dbx"),
            /// //         array("2142444E","pst"),
            /// //         array("D0CF11E0","xls/doc"),
            /// //         array("5374616E64617264204A","mdb"),
            /// //         array("FF575043","wpd"),
            /// //         array("252150532D41646F6265","eps/ps"),
            /// //         array("255044462D312E","pdf"),
            /// //         array("E3828596","pwl"),
            /// //         array("504B0304","zip"),
            /// //         array("52617221","rar"),
            /// //         array("57415645","wav"),
            /// //         array("41564920","avi"),
            /// //         array("2E7261FD","ram"),
            /// //         array("2E524D46","rm"),
            /// //         array("000001BA","mpg"),
            /// //         array("000001B3","mpg"),
            /// //         array("6D6F6F76","mov"),
            /// //         array("3026B2758E66CF11","asf"),
            /// //         array("4D546864","mid"),
            /// //         array("D3C3BBA7", 'csv')
        );
        $file = file_get_contents ( $file , 0 , NULL , 0 , 15 );
        foreach ($array as $v)
        {
            $blen = strlen(pack("H*", $v[0])); //得到文件头标记字节数
            $tbin = substr($file, 0, intval($blen)); ///需要比较文件头长度
            if(strtolower($v[0]) == strtolower(array_shift(unpack("H*", $tbin))))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否有非法数据,例如:木马、后门
     * @return bool
     */
    protected function CheckIllegal($image)
    {
        if (file_exists($image)) {
            $resource = fopen($image, 'rb');
            $fileSize = filesize($image);
            fseek($resource, 0);
            if ($fileSize > 512) { // 取头和尾
                $hexCode = bin2hex(fread($resource, 512));
                fseek($resource, $fileSize - 512);
                $hexCode .= bin2hex(fread($resource, 512));
            } else { // 取全部
                $hexCode = bin2hex(fread($resource, $fileSize));
            }
            fclose($resource);
            /* 匹配16进制中的 <% ( ) %> */
            /* 匹配16进制中的 <? ( ) ?> */
            /* 匹配16进制中的 <script | /script> 大小写亦可*/
            if (preg_match("/(3c25)|(3c3f.*?706870)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexCode)) {
                return false; //如果匹配到,说明有非法数据,就要报错,不允许上传
            }
        }
        return true;
    }

    /**
     * 判断文件大小
     * @return bool
     */
    protected function checkSize()
    {
        if ($this->size > $this->maxSize) {
            $this->setOption('errorNumber', -3);
            return false;
        }
        return true;
    }

    /**
     * 判断mime类型
     * @return bool
     */
    protected function checkMime()
    {
        if (!in_array($this->mime, $this->allowMime)) {
            $this->setOption('errorNumber', -4);
            return false;
        }
        return true;
    }

    /**
     * 判断后缀
     * @return bool
     */
    protected function checkSuffix()
    {
        if (!in_array($this->suffix, $this->allowSuffix)) {
            $this->setOption('errorNumber', -5);
            return false;
        }
        return true;
    }

    /**
     * 创建新名字
     * @return string
     */
    protected function createNewName()
    {
        if ($this->isRandName) {
            $name = $this->prefix . uniqid() . '.' . $this->suffix;
        } else {
            $name = $this->prefix . $this->oldName;
        }
        return $name;
    }

    /**
     * 读取不可访问属性的值时,__get() 会被调用。也就是,当想要获取一个类的私有属性,或者获取一个类并为定义的属性时。该魔术方法会被调用。
     * @param $name
     * @return string
     */
    public function __get($name)
    {
        if ($name == 'errorNumber') {
            return $this->errorNumber;
        } elseif ($name == 'errorInfo') {
            return $this->getErrorInfo();
        }
    }

    /**
     * 获取错误信息
     * @return string
     */
    protected function getErrorInfo()
    {
        switch ($this->errorNumber) {
            case -1:
                $str = '文件路径没有设置';
                break;
            case -2:
                $str = '文件不是目录或者不可写';
                break;
            case -3:
                $str = '文件超过指定大小';
                break;
            case -4:
                $str = 'mime类型不符合';
                break;
            case -5:
                $str = '文件后缀不符合';
                break;
            case -6:
                $str = '不是上传文件';
                break;
            case -7:
                $str = '移动失败';
                break;
            case 1:
                $str = '超出ini设置大小';
                break;
            case 2:
                $str = '超出html表单大小';
                break;
            case 3:
                $str = '文章只有部分上传';
                break;
            case 4:
                $str = '没有文件上传';
                break;
            case 6:
                $str = '找不到临时文件';
                break;
            case 7:
                $str = '文件写入失败';
                break;
            case -41:
                $str ='图片头信息类型不符合';
                break;
            case -42:
                $str ='图片内可能含有非法数据';
                break;
        }
        return $str;
    }

}

 

最后:

只需要浏览器打开 127.0.0.1/index.php 就可以完全的实现“PHP文件上传图片”功能了。

    A+
发布日期:2020年07月06日 20:02:02  所属分类:PHP
最后更新时间:2020-07-06 20:02:02
评分: (1 票;平均数5.00 ;最高评分 5 ;用户总数1;总得分 5;百分比100.00)
付杰
花牛苹果 甘肃天水 李宏恩家自种 1斤 包邮
花牛苹果 甘肃天水 李宏恩家自种 1斤 包邮
  • ¥ 6.8元
  • 市场价:8.8元
wordpress站群服务 泛解析二级域名 二级目录站群
wordpress站群服务 泛解析二级域名 二级目录站群
  • ¥ 1999.9元
  • 市场价:4800元
刷流量 刷人气 刷点击 刷收藏 刷APP关键词
刷流量 刷人气 刷点击 刷收藏 刷APP关键词
  • ¥ 1.0元
  • 市场价:9.9元
wp discux 帝国 dedecms phpcms等快速建站
wp discux 帝国 dedecms phpcms等快速建站
  • ¥ 99.9元
  • 市场价:499.9元

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: