Local.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace crmeb\services\upload\storage;
  12. use crmeb\exceptions\AdminException;
  13. use crmeb\services\DownloadImageService;
  14. use crmeb\services\upload\BaseUpload;
  15. use think\exception\ValidateException;
  16. use think\facade\Config;
  17. use think\facade\Filesystem;
  18. use think\File;
  19. use think\Image;
  20. /**
  21. * 本地上传
  22. * Class Local
  23. * @package crmeb\services\upload\storage
  24. */
  25. class Local extends BaseUpload
  26. {
  27. /**
  28. * 默认存放路径
  29. * @var string
  30. */
  31. protected $defaultPath;
  32. /**
  33. * 缩略图、水印图存放位置
  34. * @var string
  35. */
  36. public $thumbWaterPath = 'thumb_water';
  37. public function initialize(array $config)
  38. {
  39. parent::initialize($config);
  40. $this->defaultPath = Config::get('filesystem.disks.' . Config::get('filesystem.default') . '.url');
  41. $this->waterConfig['watermark_text_font'] = app()->getRootPath() . 'public' . '/statics/font/simsunb.ttf';
  42. }
  43. protected function app()
  44. {
  45. // TODO: Implement app() method.
  46. }
  47. public function getTempKeys()
  48. {
  49. // TODO: Implement getTempKeys() method.
  50. //return $this->setError('请检查您的上传配置,视频默认oss上传');
  51. return ['type' => 'local'];
  52. }
  53. /**
  54. * 生成上传文件目录
  55. * @param $path
  56. * @param null $root
  57. * @return string
  58. */
  59. public function uploadDir($path, $root = null)
  60. {
  61. if ($root === null) $root = app()->getRootPath() . 'public/';
  62. return str_replace('\\', '/', $root . 'uploads/' . $path);
  63. }
  64. /**
  65. * 检查上传目录不存在则生成
  66. * @param $dir
  67. * @return bool
  68. */
  69. protected function validDir($dir)
  70. {
  71. return is_dir($dir) == true || mkdir($dir, 0777, true) == true;
  72. }
  73. /**
  74. * 检测filepath是否是远程地址
  75. * @param string $filePath
  76. * @return bool
  77. */
  78. public function checkFilePathIsRemote(string $filePath)
  79. {
  80. return strpos($filePath, 'https:') !== false || strpos($filePath, 'http:') !== false || substr($filePath, 0, 2) === '//';
  81. }
  82. /**
  83. * 生成与配置相关的文件名称以及路径
  84. * @param string $filePath 原地址
  85. * @param string $toPath 保存目录
  86. * @param array $config 配置相关参数
  87. * @param string $root
  88. * @return string
  89. */
  90. public function createSaveFilePath(string $filePath, string $toPath, array $config = [], $root = '/')
  91. {
  92. [$path, $ext] = $this->getFileName($filePath);
  93. $fileName = md5(json_encode($config) . $filePath);
  94. return $this->uploadDir($toPath, $root) . '/' . $fileName . '.' . $ext;
  95. }
  96. /**
  97. * 文件上传
  98. * @param string $file
  99. * @return array|bool|mixed|\StdClass
  100. */
  101. public function move(string $file = 'file', $realName = false, $water = true)
  102. {
  103. $fileHandle = app()->request->file($file);
  104. if (!$fileHandle) {
  105. return $this->setError('上传的文件不存在');
  106. }
  107. if ($this->validate) {
  108. if (!in_array($fileHandle->getOriginalExtension(), $this->validate['fileExt'])) {
  109. return $this->setError('不合法的文件后缀:'.$fileHandle->getOriginalExtension());
  110. }
  111. if (filesize($fileHandle) > $this->validate['filesize']) {
  112. return $this->setError('文件过大');
  113. }
  114. if (!in_array($fileHandle->getOriginalMime(), $this->validate['fileMime'])) {
  115. return $this->setError('不合法的文件类型:'.$fileHandle->getOriginalMime());
  116. }
  117. }
  118. if ($realName) {
  119. $fileName = Filesystem::putFileAs($this->path, $fileHandle, $fileHandle->getOriginalName());
  120. } else {
  121. $fileName = Filesystem::putFile($this->path, $fileHandle);
  122. }
  123. if (!$fileName) return $this->setError('Upload failure');
  124. $filePath = Filesystem::path($fileName);
  125. $this->fileInfo->uploadInfo = new File($filePath);
  126. $this->fileInfo->realName = $fileHandle->getOriginalName();
  127. $this->fileInfo->fileName = $this->fileInfo->uploadInfo->getFilename();
  128. $this->fileInfo->filePath = $this->defaultPath . '/' . str_replace('\\', '/', $fileName);
  129. if ($this->image_thumb_status) {
  130. $this->thumb($this->fileInfo->filePath, $this->fileInfo->fileName);
  131. }
  132. return $this->fileInfo;
  133. }
  134. /**
  135. * 文件流上传
  136. * @param $fileContent
  137. * @param string|null $key
  138. * @return array|bool|mixed|\StdClass
  139. */
  140. public function stream($fileContent, string $key = null)
  141. {
  142. if (!$key) {
  143. $key = $this->saveFileName();
  144. }
  145. $dir = $this->uploadDir($this->path);
  146. if (!$this->validDir($dir)) {
  147. return $this->setError('Failed to generate upload directory, please check the permission!');
  148. }
  149. $fileName = $dir . '/' . $key;
  150. file_put_contents($fileName, $fileContent);
  151. $this->fileInfo->uploadInfo = new File($fileName);
  152. $this->fileInfo->realName = $key;
  153. $this->fileInfo->fileName = $key;
  154. $this->fileInfo->filePath = $this->defaultPath . '/' . $this->path . '/' . $key;
  155. if ($this->checkImage(public_path() . $this->fileInfo->filePath) && $this->authThumb) {
  156. try {
  157. $this->thumb($this->fileInfo->filePath, $this->fileInfo->fileName);
  158. } catch (\Throwable $e) {
  159. return $this->setError($e->getMessage());
  160. }
  161. }
  162. return $this->fileInfo;
  163. }
  164. /**
  165. * 文件流下载保存图片
  166. * @param string $fileContent
  167. * @param string|null $key
  168. * @return array|bool|mixed|\StdClass
  169. */
  170. public function down(string $fileContent, string $key = null)
  171. {
  172. if (!$key) {
  173. $key = $this->saveFileName();
  174. }
  175. $dir = $this->uploadDir($this->path);
  176. if (!$this->validDir($dir)) {
  177. return $this->setError('Failed to generate upload directory, please check the permission!');
  178. }
  179. $fileName = $dir . '/' . $key;
  180. file_put_contents($fileName, $fileContent);
  181. $this->downFileInfo->downloadInfo = new File($fileName);
  182. $this->downFileInfo->downloadRealName = $key;
  183. $this->downFileInfo->downloadFileName = $key;
  184. $this->downFileInfo->downloadFilePath = $this->defaultPath . '/' . $this->path . '/' . $key;
  185. return $this->downFileInfo;
  186. }
  187. /**
  188. * 生成缩略图
  189. * @param string $filePath
  190. * @param string $fileName
  191. * @param string $type
  192. * @return array|mixed|string[]
  193. */
  194. public function thumb(string $filePath = '', string $fileName = '', string $type = 'all')
  195. {
  196. $config = $this->thumbConfig;
  197. $data = ['big' => $filePath, 'mid' => $filePath, 'small' => $filePath];
  198. $this->fileInfo->filePathBig = $this->fileInfo->filePathMid = $this->fileInfo->filePathSmall = $this->fileInfo->filePathWater = $filePath;
  199. //地址存在且不是远程地址
  200. $filePath = str_replace(systemConfig('site_url'), '', $filePath);
  201. if ($filePath && !$this->checkFilePathIsRemote($filePath) && file_exists(root_path().'public'.$filePath)) {
  202. $findPath = str_replace($fileName, $type . '_' . $fileName, $filePath);
  203. if (file_exists('.' . $findPath)) {
  204. return [
  205. $type => str_replace($fileName, $type.'_' . $fileName, $filePath)
  206. ];
  207. }
  208. try {
  209. $this->water($filePath);
  210. foreach ($this->thumb as $v) {
  211. if ($type == 'all' || $type == $v) {
  212. $height = $v . '_height';
  213. $width = $v . '_width';
  214. $savePath = str_replace($fileName, $v . '_' . $fileName, $filePath);
  215. //防止重复生成
  216. if (!file_exists('.' . $savePath) && isset($config[$width]) && $config[$height]) {
  217. $Image = Image::open(app()->getRootPath() . 'public' . $filePath);
  218. $Image->thumb($config[$width], $config[$height])->save(root_path() . 'public' . $savePath);
  219. }
  220. $key = 'filePath' . ucfirst($v);
  221. $data[$v] = $this->fileInfo->$key = systemConfig('site_url').$savePath;
  222. }
  223. }
  224. } catch (\Throwable $e) {
  225. throw new AdminException($e->getMessage());
  226. }
  227. }
  228. return $data;
  229. }
  230. /**
  231. * 添加水印
  232. * @param string $filePath
  233. * @return mixed|string
  234. */
  235. public function water(string $filePath = '')
  236. {
  237. $waterConfig = $this->waterConfig;
  238. if ($waterConfig['image_watermark_status'] && $filePath) {
  239. switch ($waterConfig['watermark_type']) {
  240. case 1:
  241. if ($waterConfig['watermark_image']) $filePath = $this->image($filePath, $waterConfig);
  242. break;
  243. case 2:
  244. $filePath = $this->text($filePath, $waterConfig);
  245. break;
  246. }
  247. }
  248. return $filePath;
  249. }
  250. /**
  251. * 图片水印
  252. * @param string $filePath
  253. * @param array $waterConfig
  254. * @param string $waterPath
  255. * @return string
  256. */
  257. public function image(string $filePath, array $waterConfig = [])
  258. {
  259. if (!$waterConfig) {
  260. $waterConfig = $this->waterConfig;
  261. }
  262. $watermark_image = $waterConfig['watermark_image'];
  263. //远程图片
  264. $filePath = str_replace(systemConfig('site_url'), '', $filePath);
  265. if ($watermark_image && $this->checkFilePathIsRemote($watermark_image)) {
  266. //看是否在本地
  267. $pathName = $this->getFilePath($watermark_image, true);
  268. if ($pathName == $watermark_image) {//不再本地 继续下载
  269. [$p, $e] = $this->getFileName($watermark_image);
  270. $name = 'water_image_' . md5($watermark_image) . '.' . $e;
  271. $watermark_image = root_path().'public'. $this->defaultPath . '/' . $this->thumbWaterPath . '/' . $name;
  272. if (!file_exists($watermark_image)) {
  273. try {
  274. $down = app()->make(DownloadImageService::class);
  275. $data = $down->downloadImage($waterConfig['watermark_image'], $this->thumbWaterPath,$name);
  276. $watermark_image = $data['path'] ?? '';
  277. } catch (\Throwable $e) {
  278. throw new AdminException(400724);
  279. }
  280. }
  281. } else {
  282. $watermark_image = $pathName;
  283. }
  284. }
  285. if (!$watermark_image) {
  286. throw new AdminException('水印图片不存在');
  287. }
  288. $watermark_image = root_path().'public'.$watermark_image;
  289. $savePath = root_path().'public' . $filePath;
  290. try {
  291. $Image = Image::open(app()->getRootPath() . 'public' . $filePath);
  292. $Image->water($watermark_image, $waterConfig['watermark_position'] ?: 1, (int)$waterConfig['watermark_opacity'])->save($savePath);
  293. } catch (\Throwable $e) {
  294. throw new AdminException($e->getMessage());
  295. }
  296. return $savePath;
  297. }
  298. /**
  299. * 文字水印
  300. * @param string $filePath
  301. * @param array $waterConfig
  302. * @return string
  303. */
  304. public function text(string $filePath, array $waterConfig = [])
  305. {
  306. if (!$waterConfig) {
  307. $waterConfig = $this->waterConfig;
  308. }
  309. if (!$waterConfig['watermark_text']) {
  310. throw new AdminException(400723);
  311. }
  312. $savePath = public_path() . $filePath;
  313. try {
  314. $Image = Image::open(app()->getRootPath() . 'public' . $filePath);
  315. if (strlen($waterConfig['watermark_text_color']) < 7) {
  316. $waterConfig['watermark_text_color'] = substr($waterConfig['watermark_text_color'], 1);
  317. $waterConfig['watermark_text_color'] = '#' . $waterConfig['watermark_text_color'] . $waterConfig['watermark_text_color'];
  318. }
  319. if (strlen($waterConfig['watermark_text_color']) > 7) {
  320. $waterConfig['watermark_text_color'] = substr($waterConfig['watermark_text_color'], 0, 7);
  321. }
  322. $Image->text($waterConfig['watermark_text'], $waterConfig['watermark_text_font'], (float)$waterConfig['watermark_text_size'], $waterConfig['watermark_text_color'], $waterConfig['watermark_position'], $waterConfig['watermark_x'], $waterConfig['watermark_y'], $waterConfig['watermark_text_angle'])->save($savePath);
  323. } catch (\Throwable $e) {
  324. throw new AdminException($e->getMessage() . $e->getLine());
  325. }
  326. return $savePath;
  327. }
  328. /**
  329. * 删除文件
  330. * @param string $filePath
  331. * @return bool|mixed
  332. */
  333. public function delete(string $filePath)
  334. {
  335. if (file_exists($filePath)) {
  336. try {
  337. $fileArr = explode('/', $filePath);
  338. $fileName = end($fileArr);
  339. unlink($filePath);
  340. unlink(str_replace($fileName, 'big_' . $fileName, $filePath));
  341. unlink(str_replace($fileName, 'mid_' . $fileName, $filePath));
  342. unlink(str_replace($fileName, 'small_' . $fileName, $filePath));
  343. return true;
  344. } catch (\Exception $e) {
  345. return $this->setError($e->getMessage());
  346. }
  347. }
  348. return false;
  349. }
  350. public function listbuckets(string $region, bool $line = false, bool $shared = false)
  351. {
  352. return [];
  353. }
  354. public function createBucket(string $name, string $region)
  355. {
  356. return null;
  357. }
  358. public function deleteBucket(string $name)
  359. {
  360. return null;
  361. }
  362. public function setBucketCors(string $name, string $region)
  363. {
  364. return true;
  365. }
  366. public function getRegion()
  367. {
  368. return [];
  369. }
  370. public function bindDomian(string $name, string $domain, string $region = null)
  371. {
  372. return true;
  373. }
  374. public function getDomian(string $name, string $region = null)
  375. {
  376. return [];
  377. }
  378. }