图片上传-花了一天时间的bug

1.gd和imagick的区别

gd何imagick是图片处理的扩展,如缩小,裁剪,旋转,水印等。
imagick是一个面向对象的扩展,gd是提供一系列函数,所以代码编码质量imagick比gd好很多,不过php 5.3以后原生支持gd,相对来说gd更流行,也有很多gd的封装类可以使用
详细接对比访问:https://www.sitepoint.com/imagick-vs-gd/     点击打开链接

2.yii2.0框架


这个框架性能好,安全,组件化编程,生成环境使用广泛,也是作为phper必不可少去学习使用的一个框架,集成了学习优秀的设计模式和编程风格,在工作和以后的面试的利器。
简单介绍下:像容器依赖,服务定位器,组件,行为,事件,方便的getter和setter,最核心的类是component类,没接触之前对这个框架印象是框架太大,肯定性能方面有欠缺,而且不是一个很容易入门的,学习时间会相对CI,TP比较长,但这个时间值得去学习。


因为有个base64编码文件上传的需求,如果用yii框架的话只能支持type=file的文件上传,所以需要在代码上做些改动。代码如下
 public function __construct( $id, $module, $config = [] )
    {
        header("Access-Control-Allow-Origin: *");


        global $_FILES;
		if( !empty( $_POST["files"] ) && is_string($_POST['files']) )
		{

            $tmpDir = dirname( dirname( __FILE__ ) ) . "/runtime/base64files/";
            if(!file_exists($tmpDir))
            {
                @mkdir( $tmpDir ,0755,true);
            }
			$file = $tmpDir . "php" . date("YmdHi") . rand(1000,9999) . ".tmp";
            if(preg_match("/^(data:\s*image\/(\w+);base64,)/", $_POST['files'], $result))
            {

                $base_file = str_replace($result[1],"",$_POST['files']);


            }else{
                $base_file = $_POST['files'];
            }

			if( file_put_contents( $file, base64_decode( $base_file ) ) )
			{
				$ext = $this->__getImageExt( $file );

				$imageSizeInfo = @getimagesize( $file );
				if( $ext )
				{
					$fileName = date("YmdHi") . rand(10000,99999).$ext ;
					$_FILES = array (
					  'files' =>
					  array (
						'name' => $fileName,
						'type' => $imageSizeInfo[2],
						'tmp_name' => $file,
						'error' => 0,
						'size' => @filesize( $file ),
					  ),
					);
				}
				else
				{

					// TODO
                    var_dump( error_get_last());
                    exit;
				}
			}
			else
			{
				// TODO
                var_dump( error_get_last());
                exit;
			}
		}

        parent::__construct($id, $module, $config);
    }
private function __getImageExt( $file = '' )
	{
		$res = false;

		$imageSizeInfo = @getimagesize( $file ,$imageinfo);

		if( $imageSizeInfo )
		{
			$mime_type = $imageSizeInfo[2];
		
			switch ($mime_type) {
				case 1:
				case 'image/gif':
					$res = ".gif";
					break;

				case 2:
				case 'image/pjpeg':
				case 'image/jpeg':
					$res = ".jpg";
					break;

				case 3:
				case 'image/x-png':
				case 'image/png':
					$res = ".png";
					break;
				
				default:
					return false;
			}
		}

		return $res;

	}


我简单模拟$_FILES的数据结构,为了方便集成到现有的文件上传系统,代码上改动相对少很多。但是由于yii2的文件上传验证器会判断文件的传输方式,使用is_uploaded_file,传输方式我没找到方法模拟,所以不能继续用yii2提供的image验证器,所以如果有文件上传方面的规则限制,需要另外实现,逻辑也不会太复杂,

 public function validateBase64()
    {
        $rules = $this->rules();
        if(isset($rules[0]['minWidth']))
        {
            $this->minWidth = $rules[0]['minWidth'];
        }
        if(isset($rules[0]['minHeight']))
        {
            $this->minHeight = $rules[0]['minHeight'];
        }
        if(isset($rules[0]['maxSize']))
        {
            $this->maxSize = $rules[0]['maxSize'];
        }
        if(isset($rules[0]['extensions']))
        {
            $this->extensions = $rules[0]['extensions'];
        }
        if(false === strpos($this->extensions,substr($_FILES['files']['name'] ,strpos($_FILES['files']['name'] ,'.')+1)))
        {
            $error = Yii::t('yii', "Only files with these extensions are allowed: {$this->extensions}.");
            $this->addError("files", $error);
            return false;
        }

        if($_FILES['files']['size'] >$this->maxSize)
        {
            $maxSize = round($this->maxSize/(1024),1);//
            $error= Yii::t('yii', "The file  is too big. Its size cannot exceed {$maxSize} KB ");
            $this->addError("files", $error);
            return false;
        }

        $file_info = @getimagesize($_FILES['files']['tempName']);

        if($file_info)
        {
            if($file_info[0] < $this->minWidth || $file_info[1] < $this->minHeight)
            {
                $error = Yii::t('yii', "The file  is too small. Its min width cannot be smaller than {$this->minWidth} and Its min height cannot be smaller than {$this->minHeight}");
                $this->addError("files", $error);
                return false;
            }

        } else{
            $error = Yii::t('yii', 'File upload failed.');
            $this->addError("files", json_encode(error_get_last()));
            return false;
        }

        return true;
    }


保存文件的逻辑还是可以使用yii2提供的,简化代码,
 /**
     * 上传
     *
     * @param bool $tos3
     * @return bool|string
     */
    public function upload($tos3 = false)
    {


        //如果是base64上传的图片
        if($this->isBaseImageData()  )
        {
            global $_FILES;
            $this->init();

            if($this->validateBase64() && $this->save())
            {

               //上传成功后的逻辑
                return true;
            }
//            $this->addError("files", json_encode(error_get_last()));

            return false;
        }
        //正常file上传图片方式
        if ($this->validate() && $this->save()) {//

           //
  		//上传成功后的逻辑
return true; } else { return false; } }
 
   
需要注意的是在yii的saveAs方法里面需要做下处理,以便兼容base64编码图片上传,可以通过判断保存临时文件的路径
如果是文件上传用move_uploaded_file,如果使用base64编码上传,用copy方法,记得复制后删除临时文件

图片处理可以用gd,也可以用imagick,两个扩展在处理png,jpg,jpeg之类的图片没啥区别,但是在处理gif图片的时候,生成相应的缩略图,会有点问题,writeImages($fileName,true)可以写gif到$fileName,根据官方文档是没问题的,但是在调试的时候,在处理gif图片时,会报错,直接返回false,异常捕获不到,挺奇怪的,

最初认为是代码问题,查到yii框架源码才发现使用的这个imagick扩展,因为本地用的是gd扩展,所以没有发现这个问题,在测试机上会出现这个问题,日志也没有报错。把处理图片的类换成使用gd扩展的类就行了。


3.总结

查问题需要查到框架源码的时候,可以意识到因为测试环境是linux,本地是windows,不同的地方有安装的扩展不完全相同,php版本是否不同。像业务代码的bug很容易查出来,会报错,但是底层代码就需要大量时间去熟悉框架,最代码执行流程,一步一步测试






你可能感兴趣的:(图片上传-花了一天时间的bug)