UploadModel.class.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. namespace Admin\Model;
  3. use Common\Model\ModelModel;
  4. use Think\Upload;
  5. /**
  6. * 上传模型
  7. *
  8. */
  9. class UploadModel extends ModelModel
  10. {
  11. /**
  12. * 数据库表名
  13. *
  14. */
  15. protected $tableName = 'admin_upload';
  16. /**
  17. * 自动验证规则
  18. *
  19. */
  20. protected $_validate = array(
  21. array('name', 'require', '文件名不能为空', self::EXISTS_VALIDATE, 'regex', self::MODEL_BOTH),
  22. array('path', 'require', '文件不能为空', self::EXISTS_VALIDATE, 'regex', self::MODEL_BOTH),
  23. array('size', 'require', '文件大小不能为空', self::EXISTS_VALIDATE, 'regex', self::MODEL_BOTH),
  24. array('md5', 'require', '文件Md5编码不能为空', self::EXISTS_VALIDATE, 'regex', self::MODEL_BOTH),
  25. array('sha1', 'require', '文件Sha1编码不能为空', self::EXISTS_VALIDATE, 'regex', self::MODEL_BOTH),
  26. );
  27. /**
  28. * 自动完成规则
  29. *
  30. */
  31. protected $_auto = array(
  32. array('uid', 'is_login', self::MODEL_INSERT, 'function'),
  33. array('create_time', 'time', self::MODEL_INSERT, 'function'),
  34. array('update_time', 'time', self::MODEL_BOTH, 'function'),
  35. array('status', '1', self::MODEL_INSERT),
  36. );
  37. /**
  38. * 查找后置操作
  39. *
  40. */
  41. protected function _after_find(&$result, $options)
  42. {
  43. //获取上传文件的地址
  44. if ($result['url']) {
  45. $result['real_path'] = $result['url'];
  46. } else {
  47. if (C('STATIC_DOMAIN')) {
  48. $result['real_path'] = C('STATIC_DOMAIN') . $result['path'];
  49. } else {
  50. if (C('IS_API')) {
  51. $result['real_path'] = C('TOP_HOME_PAGE') . $result['path'];
  52. } else {
  53. $result['real_path'] = __ROOT__ . $result['path'];
  54. }
  55. }
  56. }
  57. if (in_array($result['ext'], array('jpg', 'jpeg', 'png', 'gif', 'bmp'))) {
  58. $result['show'] = '<img class="picture" src="' . $result['real_path'] . '">';
  59. } else {
  60. $result['show'] = '<i class="fa fa-file-' . $result['ext'] . '"></i>';
  61. }
  62. }
  63. /**
  64. * 查找后置操作
  65. *
  66. */
  67. protected function _after_select(&$result, $options)
  68. {
  69. foreach ($result as &$record) {
  70. $this->_after_find($record, $options);
  71. }
  72. }
  73. /**
  74. * 获取上传图片路径
  75. * @param int $id 文件ID
  76. * @return string
  77. *
  78. */
  79. public function getCover($id = null, $type = null)
  80. {
  81. if ($id) {
  82. // 如果以http开头直接返回
  83. if (strpos($id, "http") === 0) {
  84. return $id;
  85. }
  86. $upload_info = $this->find($id);
  87. $url = $upload_info['real_path'];
  88. }
  89. if (!isset($url)) {
  90. switch ($type) {
  91. case 'default': // 默认图片
  92. $url = C('TMPL_PARSE_STRING.__HOME_IMG__') . '/default/default.gif';
  93. break;
  94. case 'avatar': // 用户头像
  95. $url = C('TMPL_PARSE_STRING.__HOME_IMG__') . '/default/avatar.png';
  96. break;
  97. case 'qr_code': // qr_code
  98. $url = C('TMPL_PARSE_STRING.__HOME_IMG__') . '/default/qr_code.png';
  99. break;
  100. case 'qr_ios': // qr_ios
  101. $url = C('TMPL_PARSE_STRING.__HOME_IMG__') . '/default/qr_ios.png';
  102. break;
  103. case 'qr_android': // qr_android
  104. $url = C('TMPL_PARSE_STRING.__HOME_IMG__') . '/default/qr_android.png';
  105. break;
  106. case 'qr_weixin': // qr_weixin
  107. $url = C('TMPL_PARSE_STRING.__HOME_IMG__') . '/default/qr_weixin.png';
  108. break;
  109. default:
  110. $url = '';
  111. break;
  112. }
  113. }
  114. return $url;
  115. }
  116. /**
  117. * 获取上传文件信息
  118. * @param int $id 文件ID
  119. * @return string
  120. *
  121. */
  122. public function getUploadInfo($id, $field)
  123. {
  124. $upload_info = $this->where('status = 1')->find($id);
  125. if ($field) {
  126. if (!$upload_info[$field]) {
  127. return $upload_info['id'];
  128. } else {
  129. return $upload_info[$field];
  130. }
  131. }
  132. return $upload_info;
  133. }
  134. /**
  135. * 上传文件
  136. *
  137. */
  138. public function upload($files)
  139. {
  140. // 获取文件信息
  141. $_FILES = $files ? $files : $_FILES;
  142. // 返回标准数据
  143. $return = array('error' => 0, 'success' => 1, 'status' => 1);
  144. $dir = I('request.dir') ? I('request.dir') : 'image'; // 上传类型image、flash、media、file
  145. if (!in_array($dir, array('image', 'flash', 'media', 'file'))) {
  146. $return['error'] = 1;
  147. $return['success'] = 0;
  148. $return['status'] = 0;
  149. $return['message'] = '缺少上传参数!';
  150. return $return;
  151. }
  152. // 上传文件钩子,用于七牛云、又拍云等第三方文件上传的扩展
  153. hook('UploadFile', $dir);
  154. // 根据上传文件类型改变上传大小限制
  155. $upload_config = C('UPLOAD_CONFIG');
  156. if ($_GET['temp'] === 'true') {
  157. $upload_config['rootPath'] = './Runtime/';
  158. }
  159. $upload_driver = C('UPLOAD_DRIVER');
  160. if (!$upload_driver) {
  161. $return['error'] = 1;
  162. $return['success'] = 0;
  163. $return['status'] = 0;
  164. $return['message'] = '无效的文件上传驱动';
  165. return $return;
  166. }
  167. // 友情提醒
  168. $upload_max_filesize = substr(ini_get('upload_max_filesize'), 0, -1);
  169. $post_max_size = substr(ini_get('post_max_size'), 0, -1);
  170. if ($post_max_size < $upload_max_filesize) {
  171. $return['error'] = 1;
  172. $return['success'] = 0;
  173. $return['status'] = 0;
  174. $return['message'] = '警告:php.ini里post_max_size值应该设置比upload_max_filesize大';
  175. return $return;
  176. }
  177. if ($dir == 'image') {
  178. if (C('UPLOAD_IMAGE_SIZE')) {
  179. if (C('UPLOAD_IMAGE_SIZE') > $upload_max_filesize) {
  180. $return['error'] = 1;
  181. $return['success'] = 0;
  182. $return['status'] = 0;
  183. $return['message'] = '警告:php.ini里upload_max_filesize值小于系统后台设置的图片上传大小';
  184. return $return;
  185. }
  186. $upload_config['maxSize'] = C('UPLOAD_IMAGE_SIZE') * 1024 * 1024; // 图片的上传大小限制
  187. }
  188. } else {
  189. if (C('UPLOAD_FILE_SIZE')) {
  190. if (C('UPLOAD_FILE_SIZE') > $upload_max_filesize) {
  191. $return['error'] = 1;
  192. $return['success'] = 0;
  193. $return['status'] = 0;
  194. $return['message'] = '警告:php.ini里upload_max_filesize值小于系统后台设置的文件上传大小';
  195. return $return;
  196. }
  197. $upload_config['maxSize'] = C('UPLOAD_FILE_SIZE') * 1024 * 1024; // 普通文件上传大小限制
  198. }
  199. }
  200. // 上传配置
  201. $ext_arr = array(
  202. 'image' => array('gif', 'jpg', 'jpeg', 'png', 'bmp'),
  203. 'flash' => array('swf', 'flv'),
  204. 'media' => array('swf', 'flv', 'mp3', 'wav', 'wma', 'wmv', 'mid', 'avi', 'mpg', 'asf', 'rm', 'rmvb', 'mp4'),
  205. 'file' => array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'wps', 'txt', 'zip', 'rar', 'gz', 'bz2', '7z', 'ipa', 'apk', 'dmg', 'iso'),
  206. );
  207. // 计算文件散列以查看是否已有相同文件上传过
  208. $reay_file = array_shift($_FILES);
  209. $con['md5'] = md5_file($reay_file['tmp_name']);
  210. $con['sha1'] = sha1_file($reay_file['tmp_name']);
  211. $con['size'] = $reay_file['size'];
  212. $upload = $this->where($con)->find();
  213. if ($upload) {
  214. // 发现相同文件直接返回
  215. $return['id'] = $upload['id'];
  216. $return['name'] = $upload['name'];
  217. $return['url'] = $upload['real_path'];
  218. $return['path'] = '.' . $upload['path'];
  219. } else {
  220. // 上传文件
  221. $upload_config['removeTrash'] = array($this, 'removeTrash');
  222. $upload = new Upload($upload_config, $upload_driver, C("UPLOAD_{$upload_driver}_CONFIG")); // 实例化上传类
  223. $upload->exts = $ext_arr[$dir] ? $ext_arr[$dir] : $ext_arr['image']; // 设置附件上传允许的类型,注意此处$dir为空时漏洞
  224. $info = $upload->uploadOne($reay_file); // 上传文件
  225. if (!$info) {
  226. $return['error'] = 1;
  227. $return['success'] = 0;
  228. $return['status'] = 0;
  229. $return['message'] = '上传出错' . $upload->getError();
  230. } else {
  231. // 获取上传数据
  232. if ($_GET['temp'] === 'true') {
  233. $upload_data['name'] = $info["name"];
  234. $upload_data['path'] = '/Runtime/' . $info['savepath'] . $info['savename'];
  235. $upload_data['url'] = $info["url"] ?: '';
  236. // 返回数据
  237. if ($upload_data["url"]) {
  238. $return['url'] = $upload_data['url'];
  239. } else {
  240. $return['url'] = __ROOT__ . $upload_data['path'];
  241. }
  242. $return['path'] = '.' . $upload_data['path'];
  243. $return['name'] = $upload_data['name'];
  244. } else {
  245. $upload_data['type'] = $info["type"];
  246. $upload_data['name'] = $info["name"];
  247. $upload_data['path'] = '/Uploads/' . $info['savepath'] . $info['savename'];
  248. $upload_data['url'] = $info["url"] ?: '';
  249. $upload_data['ext'] = $info["ext"];
  250. $upload_data['size'] = $info["size"];
  251. $upload_data['md5'] = $info['md5'];
  252. $upload_data['sha1'] = $info['sha1'];
  253. $upload_data['location'] = $upload_driver;
  254. // 返回数据
  255. $result = $this->create($upload_data);
  256. $result = $this->add($result);
  257. if ($result) {
  258. if ($info["url"]) {
  259. $return['url'] = $upload_data['url'];
  260. } else {
  261. $return['url'] = __ROOT__ . $upload_data['path'];
  262. }
  263. $return['path'] = '.' . $upload_data['path'];
  264. $return['name'] = $upload_data['name'];
  265. $return['id'] = $result;
  266. } else {
  267. $return['error'] = 1;
  268. $return['success'] = 0;
  269. $return['status'] = 0;
  270. $return['message'] = '上传出错' . $this->error;
  271. }
  272. }
  273. }
  274. }
  275. return $return;
  276. }
  277. /**
  278. * 下载指定文件
  279. * @param number $root 文件存储根目录
  280. * @param integer $id 文件ID
  281. * @param string $args 回调函数参数
  282. * @return boolean false-下载失败,否则输出下载文件
  283. */
  284. public function download($id, $callback = null, $args = null)
  285. {
  286. // 获取下载文件信息
  287. $file = $this->find($id);
  288. if (!$file) {
  289. $this->error = '不存在该文件!';
  290. return false;
  291. }
  292. // 下载文件
  293. switch ($file['location']) {
  294. case 'Local': // 下载本地文件
  295. return $this->downLocalFile($file, $callback, $args);
  296. default:
  297. $this->error = '不支持的文件存储类型!';
  298. return false;
  299. }
  300. }
  301. /**
  302. * 下载本地文件
  303. * @param array $file 文件信息数组
  304. * @param callable $callback 下载回调函数
  305. * @param string $args 回调函数参数
  306. * @return boolean 下载失败返回false
  307. */
  308. private function downLocalFile($file, $callback = null, $args = null)
  309. {
  310. $fiel_path = '.' . $file['path'];
  311. if (file_exists($fiel_path)) {
  312. // 调用回调函数
  313. is_callable($callback) && call_user_func($callback, $args);
  314. // 新增下载数
  315. $this->where(array('id' => $file['id']))->setInc('download');
  316. // 执行下载
  317. header("Content-Description: File Transfer");
  318. header('Content-type: ' . $file['type']);
  319. header('Content-Length:' . $file['size']);
  320. if (preg_match('/MSIE/', $_SERVER['HTTP_USER_AGENT'])) {
  321. // for IE
  322. header('Content-Disposition: attachment; filename="' . rawurlencode($file['name']) . '"');
  323. } else {
  324. header('Content-Disposition: attachment; filename="' . $file['name'] . '"');
  325. }
  326. readfile($fiel_path);
  327. exit;
  328. } else {
  329. $this->error = '文件已被删除!';
  330. return false;
  331. }
  332. }
  333. }