AttachmentController.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Api\StudioApi;
  4. use App\Http\Resources\AttachmentResource;
  5. use App\Models\Attachment;
  6. use App\Services\AuthService;
  7. use FFMpeg\Coordinate\TimeCode;
  8. use FFMpeg\FFMpeg;
  9. use Illuminate\Http\File;
  10. use Illuminate\Http\Request;
  11. use Illuminate\Http\Response;
  12. use Illuminate\Support\Facades\App;
  13. use Illuminate\Support\Facades\Storage;
  14. use Illuminate\Support\Str;
  15. use Intervention\Image\ImageManagerStatic as Image;
  16. class AttachmentController extends Controller
  17. {
  18. /**
  19. * Display a listing of the resource.
  20. *
  21. * @return Response
  22. */
  23. public function index(Request $request)
  24. {
  25. //
  26. switch ($request->input('view')) {
  27. case 'studio':
  28. $user = AuthService::current($request);
  29. if (! $user) {
  30. return $this->error(__('auth.failed'));
  31. }
  32. // 判断当前用户是否有指定的studio的权限
  33. if ($user['user_uid'] !== StudioApi::getIdByName($request->input('studio'))) {
  34. return $this->error(__('auth.failed'));
  35. }
  36. $table = Attachment::where('owner_uid', $user['user_uid']);
  37. break;
  38. default:
  39. return $this->error('error view', [], 200);
  40. break;
  41. }
  42. if ($request->has('search')) {
  43. $table = $table->where('title', 'like', $request->input('search').'%');
  44. }
  45. if ($request->has('content_type')) {
  46. $table = $table->where('content_type', 'like', $request->input('content_type').'%');
  47. }
  48. $count = $table->count();
  49. $table = $table->orderBy(
  50. $request->input('order', 'updated_at'),
  51. $request->input('dir', 'desc')
  52. );
  53. $table = $table->skip($request->input('offset', 0))
  54. ->take($request->input('limit', 1000));
  55. $result = $table->get();
  56. return $this->ok(['rows' => AttachmentResource::collection($result), 'count' => $count]);
  57. }
  58. /**
  59. * Store a newly created resource in storage.
  60. *
  61. * @return Response
  62. */
  63. public function store(Request $request)
  64. {
  65. $user = AuthService::current($request);
  66. if (! $user) {
  67. return $this->error(__('auth.failed'), 401, 401);
  68. }
  69. $request->validate([
  70. 'file' => 'required',
  71. ]);
  72. $isCreate = true;
  73. if (Str::isUuid($request->input('id'))) {
  74. $attachment = Attachment::find($request->input('id'));
  75. if (! $attachment) {
  76. return $this->error('no res');
  77. }
  78. $fileId = $attachment->id;
  79. $isCreate = false;
  80. } else {
  81. $fileId = Str::uuid();
  82. }
  83. $file = $request->file('file');
  84. $bucket = config('mint.attachments.bucket_name.permanent');
  85. $ext = $file->getClientOriginalExtension();
  86. if ($request->input('type') === 'avatar') {
  87. $resize = Image::make($file)->fit(512);
  88. Storage::put($bucket.'/'.$fileId.'.jpg', $resize->stream());
  89. $resize = Image::make($file)->fit(256);
  90. Storage::put($bucket.'/'.$fileId.'_m.jpg', $resize->stream());
  91. $resize = Image::make($file)->fit(128);
  92. Storage::put($bucket.'/'.$fileId.'_s.jpg', $resize->stream());
  93. $name = $fileId.'.jpg';
  94. } else {
  95. // Move Uploaded File
  96. $name = $fileId.'.'.$ext;
  97. if (! $isCreate) {
  98. // 替换模式,先删除旧文件
  99. Storage::delete($bucket.'/'.$name);
  100. }
  101. $filename = $file->storeAs($bucket, $name);
  102. }
  103. if ($isCreate) {
  104. $attachment = new Attachment;
  105. $attachment->id = $fileId;
  106. $attachment->bucket = $bucket;
  107. if ($request->has('studio')) {
  108. $owner_uid = StudioApi::getIdByName($request->input('studio'));
  109. } else {
  110. $owner_uid = $user['user_uid'];
  111. }
  112. if ($owner_uid) {
  113. $attachment->owner_uid = $owner_uid;
  114. }
  115. $attachment->status = 'public';
  116. $path_parts = pathinfo($file->getClientOriginalName());
  117. $attachment->title = $path_parts['filename'];
  118. }
  119. $attachment->user_uid = $user['user_uid'];
  120. $attachment->name = $name;
  121. $attachment->filename = $file->getClientOriginalName();
  122. $attachment->size = $file->getSize();
  123. $attachment->content_type = $file->getMimeType();
  124. $attachment->save();
  125. $type = explode('/', $file->getMimeType());
  126. switch ($type[0]) {
  127. case 'image':
  128. $thumbnail = Image::make($file);
  129. break;
  130. case 'video':
  131. $tmpFile = $file->storeAs($bucket, $name, 'local');
  132. $path = storage_path('app/'.$tmpFile);
  133. if (App::environment('local')) {
  134. $ffmpeg = FFMpeg::create();
  135. } else {
  136. $ffmpeg = FFMpeg::create([
  137. 'ffmpeg.binaries' => '/usr/bin/ffmpeg',
  138. 'ffprobe.binaries' => '/usr/bin/ffprobe',
  139. 'timeout' => 3600,
  140. 'ffmpeg.threads' => 1,
  141. ]);
  142. }
  143. $video = $ffmpeg->open($path);
  144. $frame = $video->frame(TimeCode::fromSeconds(1));
  145. $screenShot = storage_path("app/tmp/{$fileId}.jpg");
  146. $frame->save($screenShot);
  147. $thumbnail = Image::make($screenShot);
  148. break;
  149. default:
  150. // code...
  151. break;
  152. }
  153. if (isset($thumbnail)) {
  154. // 生成缩略图
  155. $thumbnail->resize(256, 256, function ($constraint) {
  156. $constraint->aspectRatio();
  157. });
  158. Storage::put($bucket.'/'.$fileId.'_m.jpg', $thumbnail->stream());
  159. $thumbnail->resize(128, 128, function ($constraint) {
  160. $constraint->aspectRatio();
  161. });
  162. Storage::put($bucket.'/'.$fileId.'_s.jpg', $thumbnail->stream());
  163. // 销毁图片资源
  164. $thumbnail->destroy();
  165. }
  166. return $this->ok(new AttachmentResource($attachment));
  167. }
  168. /**
  169. * Display the specified resource.
  170. *
  171. * @return Response
  172. */
  173. public function show(Attachment $attachment)
  174. {
  175. //
  176. return $this->ok(new AttachmentResource($attachment));
  177. }
  178. /**
  179. * Update the specified resource in storage.
  180. *
  181. * @return Response
  182. */
  183. public function update(Request $request, Attachment $attachment)
  184. {
  185. //
  186. $user = AuthService::current($request);
  187. if (! $user) {
  188. return $this->error(__('auth.failed'), 401, 401);
  189. }
  190. $attachment->title = $request->input('title');
  191. $attachment->save();
  192. return $this->ok(new AttachmentResource($attachment));
  193. }
  194. /**
  195. * Remove the specified resource from storage.
  196. *
  197. * @return Response
  198. */
  199. public function destroy(Request $request, string $id)
  200. {
  201. //
  202. $user = AuthService::current($request);
  203. if (! $user) {
  204. return $this->error(__('auth.failed'), 401, 401);
  205. }
  206. if (Str::isUuid($id)) {
  207. $res = Attachment::where('id', $id)->first();
  208. } else {
  209. /**
  210. * 从文件名获取bucket和name
  211. */
  212. $pos = mb_strrpos($request->input('name'), '/', 0, 'UTF-8');
  213. if ($pos === false) {
  214. return $this->error('无效的文件名', 500, 500);
  215. }
  216. $bucket = mb_substr($request->input('name'), 0, $pos, 'UTF-8');
  217. $name = mb_substr($request->input('name'), $pos + 1, null, 'UTF-8');
  218. $res = Attachment::where('bucket', $bucket)
  219. ->where('name', $name)
  220. ->first();
  221. }
  222. if (! $res) {
  223. return $this->error('no res');
  224. }
  225. if ($user['user_uid'] !== $res->user_uid) {
  226. return $this->error(__('auth.failed'), 403, 403);
  227. }
  228. // 删除文件
  229. $filename = $res->bucket.'/'.$res->name;
  230. $path_parts = pathinfo($res->name);
  231. Storage::delete($filename);
  232. Storage::delete($res->bucket.'/'.$path_parts['filename'].'_m.jpg');
  233. Storage::delete($res->bucket.'/'.$path_parts['filename'].'_s.jpg');
  234. $del = $res->delete();
  235. return $this->ok($del);
  236. }
  237. }