• home > php > base >

    php抓取远程无后缀图片并上传至服务器制定目录函数—ueditor

    Author:zhoulujun Date:

    php上传远程图片,想ueditor,ckeditor等产品都有,但是,现在各种博客平台和知乎简书等,远程图片读不再是png|jpg|gif结尾,后面加一大串

    php上传远程图片,想ueditor,ckeditor等产品都有,但是,现在各种博客平台和知乎简书等,远程图片读不再是png|jpg|gif结尾,后面加一大串乱码,而且加了防盗refer。直接下载再保存太麻烦。所以写了个 获取远程图片上传的函数。

    /**
     * 抓取远程图片并上传
     * @author zhoulujun.cn
     * @version 1.0
     * @param string $img_url 远程图片url
     * @param string $file_path 删除是否成功
     * @return bool|string
     */
    
    function getRemoteImg($img_url,$file_path)
    {
        /**
         * @var array $mimes mime 和 扩展名 的映射
         */
        $mimes=array(
            'image/bmp'=>'bmp',
            'image/gif'=>'gif',
            'image/jpeg'=>'jpg',
            'image/png'=>'png',
            'image/x-icon'=>'ico'
        );
        // 获取响应头
        if(($headers=get_headers($img_url, 1))!==false)
        {
            // 获取响应的类型
            $type=$headers['Content-Type'];
            // 如果符合我们要的类型
            if(isset($mimes[$type]))
            {
    
                // 检查图片需要保持的目录是否存在 如果不存在,则立即创建一个目录
                if(!file_exists($file_path))
                {
                    mkdir($file_path,0754,TRUE);
                }
                /**
                 * @var number $file_name 文件名,这里只是演示,实际项目中请使用自己的唯一文件名生成方法
                 */
                $file_name = uniqid().rand(10000,99999);
                /**
                 * @var string $extension 确定文件后缀
                 */
                $extension=$mimes[$type];
                /**
                 * @var string $savePath 文件存储路径
                 */
                $savePath =$file_path.$file_name.'.'.$extension;
                // 获取数据并保存
                $contents=file_get_contents($img_url);
                // 存储获取图片
                if(file_put_contents($savePath , $contents))
                {
                    return $savePath;
                }
            }
        }
        return false;
    }
    
    /**
     * @example 示例
     */
    // 需要抓取的远程图片
    $img_url = "https://img-blog.csdn.net/20170722112955514?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbG9uZzUzNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"; 
    // 存储路径
    $base_dir = "./uploads/image/";
    echo getRemoteImg($img_url , $base_dir);

    ueditor 远程图片上传,无后缀不能抓取兼容

    在ueditor\php\Uploader.class.php 173行,修改。

    修改后的文件如下

    <?php
    /**
     * Created taoqili-baidu
     * UEditor编辑器通用上传类
     * @author: zhoulujun.cn
     * @verison 2.0
     */
    class Uploader
    {
        private $fileField; //文件域名
        private $file; //文件上传对象
        private $base64; //文件上传对象
        private $config; //配置信息
        private $oriName; //原始文件名
        private $fileName; //新文件名
        private $fullName; //完整文件名,即从当前配置目录开始的URL
        private $filePath; //完整文件名,即从当前配置目录开始的URL
        private $fileSize; //文件大小
        private $fileType; //文件类型
        private $stateInfo; //上传状态信息,
        private $stateMap = array( //上传状态映射表,国际化用户需考虑此处数据的国际化
            "SUCCESS", //上传成功标记,在UEditor中内不可改变,否则flash判断会出错
            "文件大小超出 upload_max_filesize 限制",
            "文件大小超出 MAX_FILE_SIZE 限制",
            "文件未被完整上传",
            "没有文件被上传",
            "上传文件为空",
            "ERROR_TMP_FILE" => "临时文件错误",
            "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
            "ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
            "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
            "ERROR_CREATE_DIR" => "目录创建失败",
            "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
            "ERROR_FILE_MOVE" => "文件保存时出错",
            "ERROR_FILE_NOT_FOUND" => "找不到上传文件",
            "ERROR_WRITE_CONTENT" => "写入文件内容错误",
            "ERROR_UNKNOWN" => "未知错误",
            "ERROR_DEAD_LINK" => "链接不可用",
            "ERROR_HTTP_LINK" => "链接不是http链接",
            "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确",
            "INVALID_URL" => "非法 URL",
            "INVALID_IP" => "非法 IP"
        );
    
        /**
         * 构造函数
         * @param string $fileField 表单名称
         * @param array $config 配置项
         * @param bool $base64 是否解析base64编码,可省略。若开启,则$fileField代表的是base64编码的字符串表单名
         */
        public function __construct($fileField, $config, $type = "upload")
        {
            $this->fileField = $fileField;
            $this->config = $config;
            $this->type = $type;
            if ($type == "remote") {
                $this->saveRemote();
            } else if($type == "base64") {
                $this->upBase64();
            } else {
                $this->upFile();
            }
    
            $this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = iconv('unicode', 'utf-8', $this->stateMap['ERROR_TYPE_NOT_ALLOWED']);
        }
    
        /**
         * 上传文件的主处理方法
         * @return mixed
         */
        private function upFile()
        {
            $file = $this->file = $_FILES[$this->fileField];
            if (!$file) {
                $this->stateInfo = $this->getStateInfo("ERROR_FILE_NOT_FOUND");
                return;
            }
            if ($this->file['error']) {
                $this->stateInfo = $this->getStateInfo($file['error']);
                return;
            } else if (!file_exists($file['tmp_name'])) {
                $this->stateInfo = $this->getStateInfo("ERROR_TMP_FILE_NOT_FOUND");
                return;
            } else if (!is_uploaded_file($file['tmp_name'])) {
                $this->stateInfo = $this->getStateInfo("ERROR_TMPFILE");
                return;
            }
    
            $this->oriName = $file['name'];
            $this->fileSize = $file['size'];
            $this->fileType = $this->getFileExt();
            $this->fullName = $this->getFullName();
            $this->filePath = $this->getFilePath();
            $this->fileName = $this->getFileName();
            $dirname = dirname($this->filePath);
    
            //检查文件大小是否超出限制
            if (!$this->checkSize()) {
                $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
                return;
            }
    
            //检查是否不允许的文件格式
            if (!$this->checkType()) {
                $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
                return;
            }
    
            //创建目录失败
            if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
                $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
                return;
            } else if (!is_writeable($dirname)) {
                $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
                return;
            }
    
            //移动文件
            if (!(move_uploaded_file($file["tmp_name"], $this->filePath) && file_exists($this->filePath))) { //移动失败
                $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
            } else { //移动成功
                $this->stateInfo = $this->stateMap[0];
            }
        }
    
        /**
         * 处理base64编码的图片上传
         * @return mixed
         */
        private function upBase64()
        {
            $base64Data = $_POST[$this->fileField];
            $img = base64_decode($base64Data);
    
            $this->oriName = $this->config['oriName'];
            $this->fileSize = strlen($img);
            $this->fileType = $this->getFileExt();
            $this->fullName = $this->getFullName();
            $this->filePath = $this->getFilePath();
            $this->fileName = $this->getFileName();
            $dirname = dirname($this->filePath);
    
            //检查文件大小是否超出限制
            if (!$this->checkSize()) {
                $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
                return;
            }
    
            //创建目录失败
            if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
                $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
                return;
            } else if (!is_writeable($dirname)) {
                $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
                return;
            }
    
            //移动文件
            if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
                $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
            } else { //移动成功
                $this->stateInfo = $this->stateMap[0];
            }
    
        }
    
        /**
         * 拉取远程图片
         * @return mixed
         */
        private function saveRemote()
        {
            $imgUrl = htmlspecialchars($this->fileField);
            $imgUrl = str_replace("&amp;", "&", $imgUrl);
    
            //http开头验证
            if (strpos($imgUrl, "http") !== 0) {
                $this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK");
                return;
            }
    
            preg_match('/(^https*:\/\/[^:\/]+)/', $imgUrl, $matches);
            $host_with_protocol = count($matches) > 1 ? $matches[1] : '';
    
            // 判断是否是合法 url
            if (!filter_var($host_with_protocol, FILTER_VALIDATE_URL)) {
                $this->stateInfo = $this->getStateInfo("INVALID_URL");
                return;
            }
    
            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)) {
                $this->stateInfo = $this->getStateInfo("INVALID_IP");
                return;
            }
           /* //关闭https证书校验
            stream_context_set_default( [
                'ssl' => [
                    'verify_host' => false,
                    'verify_peer' => false,
                    'verify_peer_name' => false,
                ],
            ]);*/
            //获取请求头并检测死链
            $heads = get_headers($imgUrl, 1);
            if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
                $this->stateInfo = $this->getStateInfo("ERROR_DEAD_LINK");
                return;
            }
            //格式验证(扩展名验证和Content-Type验证)
            $fileType = strtolower(strrchr($imgUrl, '.'));
            // url不一定带有图后缀
            //if (!in_array($fileType, $this->config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) {
            if (!isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) {
                $this->stateInfo = $this->getStateInfo("ERROR_HTTP_CONTENTTYPE");
                return;
            }
    
            //打开输出缓冲区并获取远程图片
            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();
            // 获取文件后缀
            $mimes=array(
                'image/bmp'=>'bmp',
                'image/gif'=>'gif',
                'image/jpeg'=>'jpg',
                'image/png'=>'png',
                'image/x-icon'=>'ico'
            );
            $type=$heads['Content-Type'];
            if(isset($mimes[$type])){
                $extension=$mimes[$type];
            }else{
                $extension = $this->getFileExt();
            }
            // 获取文件名
            preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $m);
            $fileName = $m ? $m[1]:"";
            // 补全文件名后缀
            if($fileName&&strlen($fileName)>4){
                $allowed = array ("gif","jpg","jpeg","png","bmp");
                $fileExt=substr($fileName,-3);
                echo in_array($fileExt,$allowed);
                if(!in_array($fileExt,$allowed)){
                    $fileName = $fileName.'.'.$extension;
                }
            }else{
                $fileName = $randNum = rand(1, 10000000000) . rand(1, 10000000000).'.'.$extension;
            }
    
            $this->oriName = $fileName;
            $this->fileSize = strlen($img);
            $this->fileType = $extension;
            $this->fullName = $this->getFullName();
            $this->filePath = $this->getFilePath();
            $this->fileName = $this->getFileName();
            $dirname = dirname($this->filePath);
    
            //检查文件大小是否超出限制
            if (!$this->checkSize()) {
                $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
                return;
            }
    
            //创建目录失败
            if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) {
                $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
                return;
            } else if (!is_writeable($dirname)) {
                $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
                return;
            }
    
            //移动文件
            if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败
                $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
            } else { //移动成功
                $this->stateInfo = $this->stateMap[0];
            }
    
        }
    
        /**
         * 上传错误检查
         * @param $errCode
         * @return string
         */
        private function getStateInfo($errCode)
        {
            return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode];
        }
    
        /**
         * 获取文件扩展名
         * @return string
         */
        private function getFileExt()
        {
            return strtolower(strrchr($this->oriName, '.'));
        }
    
        /**
         * 重命名文件
         * @return string
         */
        private function getFullName()
        {
            //替换日期事件
            $t = time();
            $d = explode('-', date("Y-y-m-d-H-i-s"));
            $format = $this->config["pathFormat"];
            $format = str_replace("{yyyy}", $d[0], $format);
            $format = str_replace("{yy}", $d[1], $format);
            $format = str_replace("{mm}", $d[2], $format);
            $format = str_replace("{dd}", $d[3], $format);
            $format = str_replace("{hh}", $d[4], $format);
            $format = str_replace("{ii}", $d[5], $format);
            $format = str_replace("{ss}", $d[6], $format);
            $format = str_replace("{time}", $t, $format);
    
            //过滤文件名的非法字符,并替换文件名
            $oriName = substr($this->oriName, 0, strrpos($this->oriName, '.'));
            $oriName = preg_replace("/[\|\?\"\<\>\/\*\\\\]+/", '', $oriName);
            $format = str_replace("{filename}", $oriName, $format);
    
            //替换随机字符串
            $randNum = rand(1, 10000000000) . rand(1, 10000000000);
            if (preg_match("/\{rand\:([\d]*)\}/i", $format, $matches)) {
                $format = preg_replace("/\{rand\:[\d]*\}/i", substr($randNum, 0, $matches[1]), $format);
            }
    
            $ext = $this->getFileExt();
            return $format . $ext;
        }
    
        /**
         * 获取文件名
         * @return string
         */
        private function getFileName () {
            return substr($this->filePath, strrpos($this->filePath, '/') + 1);
        }
    
        /**
         * 获取文件完整路径
         * @return string
         */
        private function getFilePath()
        {
            $fullname = $this->fullName;
            $rootPath = $_SERVER['DOCUMENT_ROOT'];
    
            if (substr($fullname, 0, 1) != '/') {
                $fullname = '/' . $fullname;
            }
    
            return $rootPath . $fullname;
        }
    
        /**
         * 文件类型检测
         * @return bool
         */
        private function checkType()
        {
            return in_array($this->getFileExt(), $this->config["allowFiles"]);
        }
    
        /**
         * 文件大小检测
         * @return bool
         */
        private function  checkSize()
        {
            return $this->fileSize <= ($this->config["maxSize"]);
        }
    
        /**
         * 获取当前上传成功文件的各项信息
         * @return array
         */
        public function getFileInfo()
        {
            return array(
                "state" => $this->stateInfo,
                "url" => $this->fullName,
                "title" => $this->fileName,
                "original" => $this->oriName,
                "type" => $this->fileType,
                "size" => $this->fileSize
            );
        }
    }

    其他的,稍后补充



    转载本站文章《php抓取远程无后缀图片并上传至服务器制定目录函数—ueditor》,
    请注明出处:https://www.zhoulujun.cn/html/php/phpBase/2007_0215_8159.html