UploadField.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. namespace Dcat\Admin\Form\Field;
  3. use Dcat\Admin\Traits\HasUploadedFile;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Support\Facades\Storage;
  6. use Illuminate\Support\Facades\URL;
  7. use Illuminate\Support\Facades\Validator;
  8. use Symfony\Component\HttpFoundation\File\UploadedFile;
  9. use Symfony\Component\HttpFoundation\Response;
  10. trait UploadField
  11. {
  12. use HasUploadedFile {
  13. disk as _disk;
  14. }
  15. /**
  16. * Upload directory.
  17. *
  18. * @var string
  19. */
  20. protected $directory = '';
  21. /**
  22. * File name.
  23. *
  24. * @var null
  25. */
  26. protected $name = null;
  27. /**
  28. * Storage instance.
  29. *
  30. * @var \Illuminate\Filesystem\Filesystem
  31. */
  32. protected $storage;
  33. /**
  34. * If use unique name to store upload file.
  35. *
  36. * @var bool
  37. */
  38. protected $useUniqueName = false;
  39. /**
  40. * If use sequence name to store upload file.
  41. *
  42. * @var bool
  43. */
  44. protected $useSequenceName = false;
  45. /**
  46. * Controls the storage permission. Could be 'private' or 'public'.
  47. *
  48. * @var string
  49. */
  50. protected $storagePermission;
  51. /**
  52. * @var string
  53. */
  54. protected $tempFilePath;
  55. /**
  56. * Retain file when delete record from DB.
  57. *
  58. * @var bool
  59. */
  60. protected $retainable = false;
  61. /**
  62. * @var bool
  63. */
  64. protected $saveFullUrl = false;
  65. /**
  66. * Initialize the storage instance.
  67. *
  68. * @return void.
  69. */
  70. protected function initStorage()
  71. {
  72. $this->disk(config('admin.upload.disk'));
  73. if (! $this->storage) {
  74. $this->storage = false;
  75. }
  76. }
  77. /**
  78. * If name already exists, rename it.
  79. *
  80. * @param $file
  81. *
  82. * @return void
  83. */
  84. public function renameIfExists(UploadedFile $file)
  85. {
  86. if ($this->getStorage()->exists("{$this->getDirectory()}/$this->name")) {
  87. $this->name = $this->generateUniqueName($file);
  88. }
  89. }
  90. /**
  91. * @return string
  92. */
  93. protected function getUploadPath()
  94. {
  95. return "{$this->getDirectory()}/$this->name";
  96. }
  97. /**
  98. * Get store name of upload file.
  99. *
  100. * @param UploadedFile $file
  101. *
  102. * @return string
  103. */
  104. protected function getStoreName(UploadedFile $file)
  105. {
  106. if ($this->useUniqueName) {
  107. return $this->generateUniqueName($file);
  108. }
  109. if ($this->useSequenceName) {
  110. return $this->generateSequenceName($file);
  111. }
  112. if ($this->name instanceof \Closure) {
  113. return $this->name->call($this, $file);
  114. }
  115. if (is_string($this->name)) {
  116. return $this->name;
  117. }
  118. return $file->getClientOriginalName();
  119. }
  120. /**
  121. * Get directory for store file.
  122. *
  123. * @return mixed|string
  124. */
  125. public function getDirectory()
  126. {
  127. if ($this->directory instanceof \Closure) {
  128. return call_user_func($this->directory, $this->form);
  129. }
  130. return $this->directory ?: $this->defaultDirectory();
  131. }
  132. /**
  133. * Indicates if the underlying field is retainable.
  134. *
  135. * @param bool $retainable
  136. *
  137. * @return $this
  138. */
  139. public function retainable(bool $retainable = true)
  140. {
  141. $this->retainable = $retainable;
  142. return $this;
  143. }
  144. public function saveFullUrl(bool $value = true)
  145. {
  146. $this->saveFullUrl = $value;
  147. return $this;
  148. }
  149. /**
  150. * Upload File.
  151. *
  152. * @param UploadedFile $file
  153. *
  154. * @return Response
  155. */
  156. public function upload(UploadedFile $file)
  157. {
  158. $request = request();
  159. $id = $request->get('_id');
  160. if (! $id) {
  161. return $this->responseErrorMessage(403, 'Missing id');
  162. }
  163. if ($errors = $this->getErrorMessages($file)) {
  164. return $this->responseValidationMessage($errors);
  165. }
  166. $this->name = $this->getStoreName($file);
  167. $this->renameIfExists($file);
  168. $this->prepareFile($file);
  169. if (! is_null($this->storagePermission)) {
  170. $result = $this->getStorage()->putFileAs($this->getDirectory(), $file, $this->name, $this->storagePermission);
  171. } else {
  172. $result = $this->getStorage()->putFileAs($this->getDirectory(), $file, $this->name);
  173. }
  174. if ($result) {
  175. $path = $this->getUploadPath();
  176. $url = $this->objectUrl($path);
  177. // 上传成功
  178. return $this->responseUploaded($this->saveFullUrl ? $url : $path, $url);
  179. }
  180. // 上传失败
  181. return $this->responseErrorMessage(trans('admin.uploader.upload_failed'));
  182. }
  183. /**
  184. * @param UploadedFile $file
  185. */
  186. protected function prepareFile(UploadedFile $file)
  187. {
  188. }
  189. /**
  190. * Specify the directory and name for upload file.
  191. *
  192. * @param string $directory
  193. * @param null|string $name
  194. *
  195. * @return $this
  196. */
  197. public function move($directory, $name = null)
  198. {
  199. $this->dir($directory);
  200. $this->name($name);
  201. return $this;
  202. }
  203. /**
  204. * Specify the directory upload file.
  205. *
  206. * @param string $dir
  207. *
  208. * @return $this
  209. */
  210. public function dir($dir)
  211. {
  212. if ($dir) {
  213. $this->directory = $dir;
  214. }
  215. return $this;
  216. }
  217. /**
  218. * Set name of store name.
  219. *
  220. * @param string|callable $name
  221. *
  222. * @return $this
  223. */
  224. public function name($name)
  225. {
  226. if ($name) {
  227. $this->name = $name;
  228. }
  229. return $this;
  230. }
  231. /**
  232. * Use unique name for store upload file.
  233. *
  234. * @return $this
  235. */
  236. public function uniqueName()
  237. {
  238. $this->useUniqueName = true;
  239. return $this;
  240. }
  241. /**
  242. * Use sequence name for store upload file.
  243. *
  244. * @return $this
  245. */
  246. public function sequenceName()
  247. {
  248. $this->useSequenceName = true;
  249. return $this;
  250. }
  251. /**
  252. * Generate a unique name for uploaded file.
  253. *
  254. * @param UploadedFile $file
  255. *
  256. * @return string
  257. */
  258. protected function generateUniqueName(UploadedFile $file)
  259. {
  260. return md5(uniqid()).'.'.$file->getClientOriginalExtension();
  261. }
  262. /**
  263. * Generate a sequence name for uploaded file.
  264. *
  265. * @param UploadedFile $file
  266. *
  267. * @return string
  268. */
  269. protected function generateSequenceName(UploadedFile $file)
  270. {
  271. $index = 1;
  272. $extension = $file->getClientOriginalExtension();
  273. $originalName = $file->getClientOriginalName();
  274. $newName = $originalName.'_'.$index.'.'.$extension;
  275. while ($this->getStorage()->exists("{$this->getDirectory()}/$newName")) {
  276. $index++;
  277. $newName = $originalName.'_'.$index.'.'.$extension;
  278. }
  279. return $newName;
  280. }
  281. /**
  282. * @param UploadedFile $file
  283. *
  284. * @return bool|\Illuminate\Support\MessageBag
  285. */
  286. protected function getErrorMessages(UploadedFile $file)
  287. {
  288. $rules = $attributes = [];
  289. if (! $fieldRules = $this->getRules()) {
  290. return false;
  291. }
  292. $rules[$this->column] = $fieldRules;
  293. $attributes[$this->column] = $this->label;
  294. /* @var \Illuminate\Validation\Validator $validator */
  295. $validator = Validator::make([$this->column => $file], $rules, $this->validationMessages, $attributes);
  296. if (! $validator->passes()) {
  297. $errors = $validator->errors()->getMessages()[$this->column];
  298. return implode('; ', $errors);
  299. }
  300. }
  301. /**
  302. * Destroy original files.
  303. *
  304. * @return void.
  305. */
  306. public function destroy()
  307. {
  308. $this->deleteFile($this->original);
  309. }
  310. /**
  311. * Destroy original files.
  312. *
  313. * @param $file
  314. */
  315. public function destroyIfChanged($file)
  316. {
  317. if (! $file || ! $this->original) {
  318. return $this->destroy();
  319. }
  320. $file = array_filter((array) $file);
  321. $original = (array) $this->original;
  322. $this->deleteFile(Arr::except(array_combine($original, $original), $file));
  323. }
  324. /**
  325. * Destroy files.
  326. *
  327. * @param string|array $path
  328. */
  329. public function deleteFile($paths)
  330. {
  331. if (! $paths || $this->retainable) {
  332. return;
  333. }
  334. $storage = $this->getStorage();
  335. foreach ((array) $paths as $path) {
  336. if ($storage->exists($path)) {
  337. $storage->delete($path);
  338. } else {
  339. $prefix = $storage->url('');
  340. $path = str_replace($prefix, '', $path);
  341. if ($storage->exists($path)) {
  342. $storage->delete($path);
  343. }
  344. }
  345. }
  346. }
  347. /**
  348. * Get storage instance.
  349. *
  350. * @return \Illuminate\Filesystem\Filesystem|null
  351. */
  352. public function getStorage()
  353. {
  354. if ($this->storage === null) {
  355. $this->initStorage();
  356. }
  357. return $this->storage;
  358. }
  359. /**
  360. * Set disk for storage.
  361. *
  362. * @param string $disk Disks defined in `config/filesystems.php`.
  363. *
  364. * @throws \Exception
  365. *
  366. * @return $this
  367. */
  368. public function disk($disk)
  369. {
  370. try {
  371. $this->storage = Storage::disk($disk);
  372. } catch (\Exception $exception) {
  373. if (! array_key_exists($disk, config('filesystems.disks'))) {
  374. admin_error(
  375. 'Config error.',
  376. "Disk [$disk] not configured, please add a disk config in `config/filesystems.php`."
  377. );
  378. return $this;
  379. }
  380. throw $exception;
  381. }
  382. return $this;
  383. }
  384. /**
  385. * Get file visit url.
  386. *
  387. * @param string $path
  388. *
  389. * @return string
  390. */
  391. public function objectUrl($path)
  392. {
  393. if (URL::isValidUrl($path)) {
  394. return $path;
  395. }
  396. return $this->getStorage()->url($path);
  397. }
  398. /**
  399. * @param $permission
  400. *
  401. * @return $this
  402. */
  403. public function storagePermission($permission)
  404. {
  405. $this->storagePermission = $permission;
  406. return $this;
  407. }
  408. }