💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
上传是一个网站里必不可少的功能,我们借助TP的上传类,可以方便的支持上传回调和云空间上传。 ## 编辑器上传 为了简化问题,我们只允许编辑器里上传图片。 然后编辑器配置里指定了上传的方法 ![2015-06-22/558804a303675](http://box.kancloud.cn/2015-06-22_558804a303675.png) 然后虽然对应方法只有几行代码: ~~~ public function ueditor(){ $data = new \Org\Util\Ueditor(); die($data->output()); } ~~~ 但是还要设置对应的json配置 ![2015-06-22/558805272baff](http://box.kancloud.cn/2015-06-22_558805272baff.png) 如对应的保存目录、后缀等规则。 这里注意,编辑器上传的类里把不同文件类型的划分了不同的action。 ![2015-06-22/558805c82074d](http://box.kancloud.cn/2015-06-22_558805c82074d.png) 而我的图片上传插件plupload由于返回和系统上传的不一样,我干脆就利用了编辑器上传,通过穿参上传图片。 ![2015-06-22/5588062d34ead](http://box.kancloud.cn/2015-06-22_5588062d34ead.png) ~~~ public function plupload(){ $data = new \Org\Util\Ueditor(); $json = $data->output(); $result = json_decode($json, 1); if('SUCCESS' == $result['state']){ $output = array( 'ok'=>1, 'path'=>$result['url'], ); }else{ $output = array( 'ok'=>0, 'code'=>$result['state'], ); } die(json_encode($output)); } ~~~ ## 系统上传 系统上传主要把上传的对象定位图片和文件两种类型。 然后参照OneThink,给不同类型设置不同的驱动和上传配置。两种类型的文件分别存在File表和Picture表里。 上传配置: ~~~ 'PICTURE_UPLOAD_DRIVER'=>'Local', 'FILE_UPLOAD_DRIVER'=>'Local', /* 文件上传相关配置 */ 'FILE_UPLOAD' => array( 'mimes' => '', //允许上传的文件MiMe类型 'maxSize' => 1024*1024*1024, //上传的文件大小限制 (0-不做限制) 'exts' => 'jpg,gif,png,jpeg,zip,rar,tar,gz,7z,doc,docx,txt,xml,mp3,mp4', //允许上传的文件后缀 'autoSub' => true, //自动子目录保存文件 'subName' => array('date', 'Y-m-d'), //子目录创建方式,[0]-函数名,[1]-参数,多个参数使用数组 'rootPath' => './Uploads/file/', //保存根路径 'savePath' => '', //保存路径 'saveName' => array('uniqid', ''), //上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组 'saveExt' => '', //文件保存后缀,空则使用原后缀 'replace' => false, //存在同名是否覆盖 'hash' => true, //是否生成hash编码 'callback' => false, //检测文件是否存在回调函数,如果存在返回文件信息数组 ), //下载模型上传配置(文件上传类配置) /* 图片上传相关配置 */ 'PICTURE_UPLOAD' => array( 'mimes' => '', //允许上传的文件MiMe类型 'maxSize' => 2*1024*1024, //上传的文件大小限制 (0-不做限制) 'exts' => 'jpg,gif,png,jpeg,tmp', //允许上传的文件后缀 'autoSub' => true, //自动子目录保存文件 'subName' => array('date', 'Y-m-d'), //子目录创建方式,[0]-函数名,[1]-参数,多个参数使用数组 'rootPath' => './Uploads/picture/', //保存根路径 'savePath' => '', //保存路径 'saveName' => array('uniqid', ''), //上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组 'saveExt' => '', //文件保存后缀,空则使用原后缀 'replace' => false, //存在同名是否覆盖 'hash' => true, //是否生成hash编码 'callback' => false, //检测文件是否存在回调函数,如果存在返回文件信息数组 ), //图片上传相关配置(文件上传类配置) ~~~ 上传主要代码: ~~~ public function ajaxUpload($model= 'File',$field='file') { $params = array('model'=>$model, 'field'=>$field); slog($params); $result = $this->upload($params); if ($result['status']) { $result['info'] = '上传成功'; } else { $result['status'] = 0; $result['info'] = '上传失败'.$result['info']; } $this->ajaxReturn($result); } protected function upload($params) { slog($params); /* 返回标准数据 */ $return = array('status' => 1, 'info' => '上传成功', 'data' => ''); $model = $params['model'] == 'Picture'? 'Picture' : 'File'; /* 调用文件上传组件上传文件 */ $table = D($model); $driver = C("{$model}_UPLOAD_DRIVER"); d_f('upload', $driver); $upload_config = C("{$model}_UPLOAD"); d_f('upload', $upload_config); if(isset($params['rootPath'])) $upload_config['rootPath'] = $params['rootPath']; $info = $table->upload($_FILES, $upload_config, $driver, C("UPLOAD_{$driver}_CONFIG")); d_f('upload', $info); /* 记录图片信息 */ if ($info) { $return['status'] = 1; $return = array_merge($info[$params['field']], $return); if (method_exists($this, 'after_upload')) { $this->after_upload(CONTROLLER_NAME, $info, $result); } } else { $return['status'] = 0; $return['info'] = $table->getError(); } d_f('upload', $return); return $return; } ~~~ 先是封装了一个上传方法,并且支持回调`after_upload`。 然后,封装了一个系统用的ajaxUpload 方法用于返回json响应。 上传方法中调用对应模型的upload方法,比如FileModel的upload ~~~ /** * 文件上传 * @param array $files 要上传的文件列表(通常是$_FILES数组) * @param array $setting 文件上传配置 * @param string $driver 上传驱动名称 * @param array $config 上传驱动配置 * @return array 文件上传成功后的信息 */ public function upload($files, $setting, $driver = 'Local', $config = null){ /* 上传文件 */ $setting['callback'] = array($this, 'isFile'); $setting['removeTrash'] = array($this, 'removeTrash'); $Upload = new Upload($setting, $driver, $config); $info = $Upload->upload($files); /* 设置文件保存位置 */ $this->_auto[] = array('location', 'ftp' === strtolower($driver) ? 1 : 0, self::MODEL_INSERT); if($info){ //文件上传成功,记录文件信息 foreach ($info as $key => &$value) { /* 已经存在文件记录 */ if(isset($value['id']) && is_numeric($value['id'])){ $value['path'] = substr($setting['rootPath'], 1).$value['savepath'].$value['savename']; //在模板里的url路径 continue; } /* 记录文件信息 */ $value['path'] = substr($setting['rootPath'], 1).$value['savepath'].$value['savename']; //在模板里的url路径 if($this->create($value) && ($id = $this->add())){ $value['id'] = $id; } else { //TODO: 文件上传成功,但是记录文件信息失败,需记录日志 unset($info[$key]); } } return $info; //文件上传成功 } else { $this->error = $Upload->getError(); return false; } } ~~~ 方法进行了文件重复的上传判断和上传完毕后入库的操作。 为了方便在没有slog的时候调试。我还写了一个`d_f`函数 替代f函数写文件记录调试信息。 ~~~ /** * 快速文件数据读取和保存 针对简单类型数据 字符串、数组 * @param string $name 缓存名称 * @param mixed $value 缓存值 * @param string $path 缓存路径 * @return mixed */ function d_f($name, $value, $path = DATA_PATH) { static $_cache = array(); $filename = $path . $name . '.php'; if ('' !== $value) { if (is_null($value)) { // 删除缓存 // return false !== strpos($name,'*')?array_map("unlink", glob($filename)):unlink($filename); } else { // 缓存数据 $dir = dirname($filename); // 目录不存在则创建 if (!is_dir($dir)) mkdir($dir, 0755, true); $_cache[$name] = $value; $content = strip_whitespace("<?php\treturn " . var_export($value, true) . ";?>") . PHP_EOL; return file_put_contents($filename, $content, FILE_APPEND); } } if (isset($_cache[$name])) return $_cache[$name]; // 获取缓存数据 if (is_file($filename)) { $value = include $filename; $_cache[$name] = $value; } else { $value = false; } return $value; } ~~~ 他比f函数直接写日志的优势是,追加记录文件。 像这样 ![2015-06-22/558809f9db5e9](http://box.kancloud.cn/2015-06-22_558809f9db5e9.png) 默认记录在runtime 下data目录里。