<?php
/**
* @author 龙虾 <15108148@qq.com>
* @version 2.0 2020-08-27
* @copyright 睿谷信息 <www.rgxx.com>
*/
namespace app\common\controller;
use think\Controller;
use app\common\model\Media;
use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Storage\BucketManager;
use Qiniu\Storage\UploadManager;

class Ueditor extends Controller
{
	private $uppath;// 上传路径
	private $water;
	private $thumb;
	protected function initialize()
    {
        parent::initialize();
        if(empty(session('userid')) && empty(session('adminid'))) $this->error('拒绝访问');
        $this->uppath = './'.config('app.upload.uppath');
        $water = is_true(input('param.water'));
        $thumb = is_true(input('param.thumb'));
        $this->water = $water ? config('app.image.wateronoff') : false;
        $this->thumb = $thumb ? config('app.image.thumbonoff') : false;
    }
	//接口
	public function upload()
	{
		//halt(input('param.water'));
		//header('Access-Control-Allow-Origin: http://www.baidu.com'); //设置http://www.baidu.com允许跨域访问
        //header('Access-Control-Allow-Headers: X-Requested-With,X_Requested_With'); //设置允许的跨域header
		date_default_timezone_set("Asia/Chongqing");
		error_reporting(E_ERROR);
		header("Content-Type: text/html; charset=utf-8");

		$CONFIG = config('upload.');
		$action = input('param.action');
		switch ($action) {
		    case 'config':
		        $result =  json_encode($CONFIG);
		        break;

		    /* 上传图片 */
		    case 'uploadimage':
		    	$config = array(
		            "maxSize" => config('app.upload.imageMaxSize')*1024*1024,
		            "allowFiles" => str_replace("|",",",config('app.upload.imageAllowFiles')),
		            "filetype" => "image"
		        );
		        $fieldName = $CONFIG['imageFieldName'];
		        $result = $this->upFile($config, $fieldName);
		        break;
		    /* 上传视频 */
		    case 'uploadvideo':
		    	$config = array(
		            "maxSize" => config('app.upload.videoMaxSize')*1024*1024,
		            "allowFiles" => str_replace("|",",",config('app.upload.videoAllowFiles')),
		            "filetype" => "video"
		        );
		        $fieldName = $CONFIG['videoFieldName'];
		        $result = $this->upFile($config, $fieldName);
		        break;
		    /* 上传文件 */
		    case 'uploadfile':
		    	$config = array(
		            "maxSize" => config('app.upload.fileMaxSize')*1024*1024,
		            "allowFiles" => str_replace("|",",",config('app.upload.fileAllowFiles')),
		            "filetype" => "file"
		        );
		        $fieldName = $CONFIG['fileFieldName'];
		        $result = $this->upFile($config, $fieldName);
		        break;
		    /* 列出图片 */
		    case 'listimage':
		    	$allowFiles = $CONFIG['imageManagerAllowFiles'];
			    $listSize = $CONFIG['imageManagerListSize'];
			    $result =$this->fileList($allowFiles,$listSize,'image');
                break;
		    /* 列出文件 */
		    case 'listfile':
		        $allowFiles = $CONFIG['fileManagerAllowFiles'];
			    $listSize = $CONFIG['fileManagerListSize'];
			    $result = $this->fileList($allowFiles,$listSize,'file');
                break;
		    /* 抓取远程文件 */
		    case 'catchimage':
		        $config = array(
				    "maxSize" => config('app.upload.imageMaxSize')*1024*1024,
				    "allowFiles" => explode('|',config('app.upload.imageAllowFiles')),
				    "oriName" => "remote.png"
			    );
			    $fieldName = $CONFIG['catcherFieldName'];
			    /* 抓取远程图片 */
			    $list = array();
			    isset($_POST[$fieldName]) ? $source = $_POST[$fieldName] : $source = $_GET[$fieldName];
				
			    foreach($source as $imgUrl){
			        $info = json_decode($this->saveRemote($config,$imgUrl),true);
			        array_push($list, array(
			            "state" => $info["state"],
			            "url" => $info["url"],
			            "size" => $info["size"],
			            "title" => htmlspecialchars($info["title"]),
			            "original" => htmlspecialchars($info["original"]),
			            "source" => htmlspecialchars($imgUrl)
			        ));
			    }

			    $result = json_encode(array(
			        'state' => count($list) ? 'SUCCESS':'ERROR',
			        'list' => $list
			    ));
		        break;

		    default:
		        $result = json_encode(array(
		            'state'=> lang('requestfalse')
		        ));
		        break;
		}
		/* 输出结果 */
		if (isset($_GET["callback"])) {
		    if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
		        echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
		    } else {
		        echo json_encode(array(
		            'state'=> lang('paramerror')
		        ));
		    }
		} else {
		    echo $result;
		}
	}

	// 上传处理
	private function upFile($config,$fieldName)
	{
		$file = request()->file($fieldName);
		$file_md5 = md5_file($file->getInfo('tmp_name'));
		// 查询库中是否有素材
		$has_media = Media::whereFilemd5($file_md5)->find();
		if(!$has_media) {
			if(config('app.qiniu.qiniu')) {
				// 初始化签权对象
                $auth = new Auth(config('app.qiniu.AK'),config('app.qiniu.SK'));
                $token = $auth->uploadToken(config('app.qiniu.bucket'));
                // 构建 UploadManager 对象
                $uploadMrg = new UploadManager();
                // 上传文件到七牛
                $ext = pathinfo($file->getInfo('name'), PATHINFO_EXTENSION);
                $fname=date('Ymd').'/'.substr(md5($file->getRealPath()) , 0, 5). date('YmdHis') . rand(0, 9999) . '.' . $ext;
                list($ret, $err) = $uploadMrg->putFile($token, $fname, $file->getInfo('tmp_name'));
                
                if ($err !== null) {
                	$data=array(
					    'state' => '七牛错误，请检查七牛配置',
					);
                } else {
                	$data=array(
						'state' => 'SUCCESS',
						'url' => config('app.qiniu.qiniudomain') . '/' . $ret['key'],
						'title' => $file->getInfo('name'),
						'original' => $file->getInfo('name'),
						'type' => $config['filetype'],
						'size' => $file->getSize(),
						'filemd5' => $file_md5
					);
					Media::create($data);
                }
			} else {
				$info = $file->validate(['size'=>$config['maxSize'],'ext'=>$config['allowFiles']])->move($this->uppath.'/'.$config['filetype']);
				if($info) {
					$fname='/'.config('app.upload.uppath').'/'.$config['filetype'].'/'.str_replace('\\','/',$info->getSaveName());

					$imgArr = explode('|',strtolower(config('app.upload.imageAllowFiles')));
					$imgExt= strtolower($info->getExtension());
					$isImg = in_array($imgExt,$imgArr);
					// 如果是图片
					if($isImg) {
						(new \rg\Image(['do_water'=>$this->water,'do_thumb'=>$this->thumb]))->make($fname);
					}
					$data=array(
						'state' => 'SUCCESS',
						'url' => $fname,
						'title' => $info->getFilename(),
						'original' => $info->getInfo('name'),
						'type' => $config['filetype'],
						'size' => $info->getSize(),
						'filemd5' => $file_md5
					);
					Media::create($data);
				}else{
					$data=array(
					    'state' => $file->getError(),
					);
				}
			}
			
		} else {
			if(config('app.qiniu.qiniu') || $has_media->getData('type') !== 'image' || (!config('app.image.thumbonoff') && !config('app.image.wateronoff'))) { // 处理非图片，或者缩略图和水印都关闭的情况
				$data=array(
					'state' => 'SUCCESS',
					'url' => $has_media['url'],
					'title' => $has_media['title'],
					'original' => $has_media['original'],
					'type' => $has_media['type'],
					'size' => $has_media['size'],
					'filemd5' => $has_media['filemd5']
				);
			} else {
				$info = $file->validate(['size'=>$config['maxSize'],'ext'=>$config['allowFiles']])->move('.'.dirname($has_media['url']),basename($has_media['url']));
				if($info) {
					(new \rg\Image(['do_water'=>$this->water,'do_thumb'=>$this->thumb]))->make($has_media['url']);
					$data=array(
						'state' => 'SUCCESS',
						'url' => $has_media['url'],
						'title' => $info->getFilename(),
						'original' => $info->getInfo('name'),
						'type' => $config['filetype'],
						'size' => $info->getSize(),
						'filemd5' => $file_md5
					);
				} else {
					$data=array(
					    'state' => $file->getError(),
					);
				}
			}
		}
		
		return json_encode($data);
	}

	// 文件列表
	private function fileList($allowFiles,$listSize,$dir)
	{
		$dirname = './'.config('app.upload.uppath').'/'.$dir.'/';
		$allowFiles = substr(str_replace(".","|",join("",$allowFiles)),1);

		/* 获取参数 */
		$size = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : $listSize;
		$start = isset($_GET['start']) ? htmlspecialchars($_GET['start']) : 0;
		$end = $start + $size;

		/* 获取文件列表 */
		$path = $dirname;
		$files = $this->getFiles($path,$allowFiles);
		if(!count($files)){
		    return json_encode(array(
		        "state" => "no match file",
		        "list" => array(),
		        "start" => $start,
		        "total" => count($files)
		    ));
		}

		/* 获取指定范围的列表 */
		$len = count($files);
		for($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){
		    $list[] = $files[$i];
		}

		/* 返回数据 */
		$result = json_encode(array(
		    "state" => "SUCCESS",
		    "list" => $list,
		    "start" => $start,
		    "total" => count($files)
		));

		return $result;
	}

	/*
	 * 遍历获取目录下的指定类型的文件
	 * @param $path
	 * @param array $files
	 * @return array
	*/
    private function getFiles($path,$allowFiles,&$files = array())
    {
	    if(!is_dir($path)) return null;
	    if(substr($path,strlen($path)-1) != '/') $path .= '/';
	    $handle = opendir($path);
			
	    while(false !== ($file = readdir($handle))){
	        if($file != '.' && $file != '..'){
	            $path2 = $path.$file;
	            if(is_dir($path2)){
	                $this->getFiles($path2,$allowFiles,$files);
	            }else{
		            if(preg_match("/\.(".$allowFiles.")$/i",$file)){
		                $files[] = array(
		                    'url' => substr($path2,1),
		                    'mtime' => filemtime($path2)
		                );
		            }
	            }
	        }
	    }
		
	    return $files;
    }

    //抓取远程图片
	private function saveRemote($config,$fieldName)
	{
		set_time_limit(0);
	    $imgUrl = htmlspecialchars($fieldName);
	    $imgUrl = str_replace("&amp;","&",$imgUrl);

	    //http开头验证
	    if(strpos($imgUrl,"http") !== 0){
	        $data=array(
		        'state' => '链接不是http链接'
		    );
	        return json_encode($data);
	    }

	    preg_match('/(^https*:\/\/[^:\/]+)/', $imgUrl, $matches);
        $host_with_protocol = count($matches) > 1 ? $matches[1] : '';

        // 判断是否是合法 url
        if (!filter_var($host_with_protocol, FILTER_VALIDATE_URL)) {
            $data=array(
		        'state' => '非法URL'
		    );
	        return json_encode($data);
        }
        preg_match('/^https*:\/\/(.+)/', $host_with_protocol, $matches);
        $host_without_protocol = count($matches) > 1 ? $matches[1] : '';

        // 此时提取出来的可能是 ip 也有可能是域名，先获取 ip
        $ip = gethostbyname($host_without_protocol);
        // 判断是否是私有 ip
        if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
            $data=array(
		        'state' => '非法IP'
		    );
	        return json_encode($data);
        }

	    //获取请求头并检测死链
	    $heads = get_headers($imgUrl, 1);
	    if(!(stristr($heads[0],"200") && stristr($heads[0],"OK"))){
	        $data=array(
		        'state' => '链接不可用'
		    );
	        return json_encode($data);
	    }
	    //格式验证(扩展名验证和Content-Type验证)
	    $fileType = str_replace('.','',strtolower(strrchr($imgUrl,'.')));
	    if(!in_array($fileType,$config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) {
	        $data=array(
		        'state' => '链接contentType不正确'
		    );
	        return json_encode($data);
	    }

	    //打开输出缓冲区并获取远程图片
	    ob_start();
	    $context = stream_context_create(
	        array('http' => array(
	            'follow_location' => false // don't follow redirects
	        ))
	    );
	    readfile($imgUrl,false,$context);
	    $img = ob_get_contents();
	    ob_end_clean();
	    preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/",$imgUrl,$m);

	    $dirname = './'.config('app.upload.uppath').'/remote/';
	    $file['oriName'] = $m ? $m[1] : "";
	    $file['filesize'] = strlen($img);
	    $file['ext'] = strtolower(strrchr($config['oriName'],'.'));
	    $file['name'] = uniqid().$file['ext'];
	    $file['fullName'] = $dirname.$file['name'];
	    $fullName = $file['fullName'];

	    //检查文件大小是否超出限制
	    if($file['filesize'] >= ($config["maxSize"])){
  		    $data=array(
			    'state' => '文件大小超出网站限制'
		    );
		    return json_encode($data);
	    }

	    //创建目录失败
	    if(!file_exists($dirname) && !mkdir($dirname,0777,true)){
  		    $data=array(
			    'state' => '目录创建失败'
		    );
		    return json_encode($data);
	    }else if(!is_writeable($dirname)){
  		    $data=array(
			    'state' => '目录没有写权限'
		    );
		    return json_encode($data);
	    }

	    //移动文件
	    if(!(file_put_contents($fullName, $img) && file_exists($fullName))){ //移动失败
  		    $data=array(
			    'state' => '写入文件内容错误'
		    );
		    return json_encode($data);
	    }else{ //移动成功
	        $data=array(
			    'state' => 'SUCCESS',
			    'url' => substr($file['fullName'],1),
			    'title' => $file['name'],
			    'original' => $file['oriName'],
			    'type' => 'remote',
			    'size' => $file['filesize'],
		    );
	    }
		
	    return json_encode($data);
	}
}