UploadField.php 10 KB

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