Upload.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <?php
  2. namespace app\common\library;
  3. use ba\Random;
  4. use think\File;
  5. use think\Exception;
  6. use think\facade\Config;
  7. use think\file\UploadedFile;
  8. use app\common\model\Attachment;
  9. /**
  10. * 上传
  11. */
  12. class Upload
  13. {
  14. /**
  15. * 配置信息
  16. * @var array
  17. */
  18. protected $config = [];
  19. /**
  20. * @var UploadedFile
  21. */
  22. protected $file = null;
  23. /**
  24. * 是否是图片
  25. * @var bool
  26. */
  27. protected $isImage = false;
  28. /**
  29. * 文件信息
  30. * @var null
  31. */
  32. protected $fileInfo = null;
  33. /**
  34. * 细目
  35. * @var string
  36. */
  37. protected $topic = 'default';
  38. /**
  39. * 构造方法
  40. * @param UploadedFile|null $file
  41. * @param array $config
  42. * @throws Exception
  43. */
  44. public function __construct(UploadedFile $file = null, array $config = [])
  45. {
  46. $this->config = Config::get('upload');
  47. if ($config) {
  48. $this->config = array_merge($this->config, $config);
  49. }
  50. if ($file) {
  51. $this->setFile($file);
  52. }
  53. }
  54. /**
  55. * 设置文件
  56. * @param UploadedFile $file
  57. * @throws Exception
  58. */
  59. public function setFile(UploadedFile $file)
  60. {
  61. if (empty($file)) {
  62. throw new Exception(__('No files were uploaded'), 10001);
  63. }
  64. $suffix = strtolower($file->extension());
  65. $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
  66. $fileInfo['suffix'] = $suffix;
  67. $fileInfo['type'] = $file->getOriginalMime();
  68. $fileInfo['size'] = $file->getSize();
  69. $fileInfo['name'] = $file->getOriginalName();
  70. $fileInfo['sha1'] = $file->sha1();
  71. $this->file = $file;
  72. $this->fileInfo = $fileInfo;
  73. }
  74. /**
  75. * 检查文件类型
  76. * @return bool
  77. * @throws Exception
  78. */
  79. protected function checkMimetype(): bool
  80. {
  81. $mimetypeArr = explode(',', strtolower($this->config['mimetype']));
  82. $typeArr = explode('/', $this->fileInfo['type']);
  83. //验证文件后缀
  84. if ($this->config['mimetype'] === '*'
  85. || in_array($this->fileInfo['suffix'], $mimetypeArr) || in_array('.' . $this->fileInfo['suffix'], $mimetypeArr)
  86. || in_array($this->fileInfo['type'], $mimetypeArr) || in_array($typeArr[0] . "/*", $mimetypeArr)) {
  87. return true;
  88. }
  89. throw new Exception(__('The uploaded file format is not allowed'), 10002);
  90. }
  91. /**
  92. * 是否是图片并设置好相关属性
  93. * @return bool
  94. * @throws Exception
  95. */
  96. protected function checkIsImage(): bool
  97. {
  98. if (in_array($this->fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($this->fileInfo['suffix'], ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) {
  99. $imgInfo = getimagesize($this->file->getPathname());
  100. if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) {
  101. throw new Exception(__('The uploaded image file is not a valid image'));
  102. }
  103. $this->fileInfo['width'] = $imgInfo[0];
  104. $this->fileInfo['height'] = $imgInfo[1];
  105. $this->isImage = true;
  106. return true;
  107. }
  108. return false;
  109. }
  110. /**
  111. * 上传的文件是否为图片
  112. * @return bool
  113. */
  114. public function isImage(): bool
  115. {
  116. return $this->isImage;
  117. }
  118. /**
  119. * 检查文件大小
  120. * @throws Exception
  121. */
  122. protected function checkSize()
  123. {
  124. $size = file_unit_to_byte($this->config['maxsize']);
  125. if ($this->fileInfo['size'] > $size) {
  126. throw new Exception(__('The uploaded file is too large (%sMiB), Maximum file size:%sMiB', [
  127. round($this->fileInfo['size'] / pow(1024, 2), 2),
  128. round($size / pow(1024, 2), 2)
  129. ]));
  130. }
  131. }
  132. /**
  133. * 获取文件后缀
  134. * @return mixed|string
  135. */
  136. public function getSuffix()
  137. {
  138. return $this->fileInfo['suffix'] ?: 'file';
  139. }
  140. /**
  141. * 获取文件保存名
  142. * @param string|null $saveName
  143. * @param string|null $filename
  144. * @param string|null $sha1
  145. * @return array|mixed|string|string[]
  146. */
  147. public function getSaveName(string $saveName = null, string $filename = null, string $sha1 = null)
  148. {
  149. if ($filename) {
  150. $suffix = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
  151. $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
  152. } else {
  153. $suffix = $this->fileInfo['suffix'];
  154. }
  155. $filename = $filename ?: ($suffix ? substr($this->fileInfo['name'], 0, strripos($this->fileInfo['name'], '.')) : $this->fileInfo['name']);
  156. $sha1 = $sha1 ?: $this->fileInfo['sha1'];
  157. $replaceArr = [
  158. '{topic}' => $this->topic,
  159. '{year}' => date("Y"),
  160. '{mon}' => date("m"),
  161. '{day}' => date("d"),
  162. '{hour}' => date("H"),
  163. '{min}' => date("i"),
  164. '{sec}' => date("s"),
  165. '{random}' => Random::build(),
  166. '{random32}' => Random::build('alnum', 32),
  167. '{filename}' => substr($filename, 0, 100),
  168. '{suffix}' => $suffix,
  169. '{.suffix}' => $suffix ? '.' . $suffix : '',
  170. '{filesha1}' => $sha1,
  171. ];
  172. $saveName = $saveName ?: $this->config['savename'];
  173. return str_replace(array_keys($replaceArr), array_values($replaceArr), $saveName);
  174. }
  175. /**
  176. * 上传文件
  177. * @param string|null $saveName
  178. * @param int $adminId
  179. * @param int $userId
  180. * @return array
  181. * @throws Exception
  182. */
  183. public function upload(string $saveName = null, int $adminId = 0, int $userId = 0): array
  184. {
  185. if (empty($this->file)) {
  186. throw new Exception(__('No files have been uploaded or the file size exceeds the upload limit of the server'));
  187. }
  188. $this->checkSize();
  189. $this->checkMimetype();
  190. $this->checkIsImage();
  191. $params = [
  192. 'topic' => $this->topic,
  193. 'admin_id' => $adminId,
  194. 'user_id' => $userId,
  195. 'url' => $this->getSaveName(),
  196. 'width' => $this->fileInfo['width'] ?? 0,
  197. 'height' => $this->fileInfo['height'] ?? 0,
  198. 'name' => substr(htmlspecialchars(strip_tags($this->fileInfo['name'])), 0, 100),
  199. 'size' => $this->fileInfo['size'],
  200. 'mimetype' => $this->fileInfo['type'],
  201. 'storage' => 'local',
  202. 'sha1' => $this->fileInfo['sha1']
  203. ];
  204. $attachment = new Attachment();
  205. $attachment->data(array_filter($params));
  206. $res = $attachment->save();
  207. if (!$res) {
  208. $attachment = Attachment::where([
  209. ['sha1', '=', $params['sha1']],
  210. ['topic', '=', $params['topic']],
  211. ['storage', '=', $params['storage']],
  212. ])->find();
  213. } else {
  214. $this->move($saveName);
  215. }
  216. return $attachment->toArray();
  217. }
  218. public function move($saveName = null): File
  219. {
  220. $saveName = $saveName ?: $this->getSaveName();
  221. $saveName = '/' . ltrim($saveName, '/');
  222. $uploadDir = substr($saveName, 0, strripos($saveName, '/') + 1);
  223. $fileName = substr($saveName, strripos($saveName, '/') + 1);
  224. $destDir = root_path() . 'public' . str_replace('/', DIRECTORY_SEPARATOR, $uploadDir);
  225. return $this->file->move($destDir, $fileName);
  226. }
  227. }