| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- <?php
- namespace App\Http\Controllers;
- use App\Http\Controllers\Controller;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\Storage;
- use Intervention\Image\Facades\Image;
- use Illuminate\Support\Str;
- class FileUploadController extends Controller
- {
- // 允许的文件类型
- private $allowedMimes = [
- 'image' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
- 'document' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
- 'video' => ['mp4', 'avi', 'mov', 'wmv'],
- 'audio' => ['mp3', 'wav', 'ogg'],
- ];
-
- // 文件大小限制(单位:MB)
- private $maxSizes = [
- 'image' => 10,
- 'document' => 20,
- 'video' => 100,
- 'audio' => 50,
- ];
- /**
- * 获取当前认证的用户
- */
- protected function getAuthenticatedUser()
- {
- return request()->user; // 从中间件注入
- }
- /**
- * 获取用户类型
- */
- protected function getUserType()
- {
- return request()->user_type; // 从中间件注入
- }
- /**
- * 获取用户ID
- */
- protected function getUserId()
- {
- $user = $this->getAuthenticatedUser();
- return $user ? $user->id : null;
- }
- /**
- * 保存文件记录
- */
- private function saveFileRecord($request, $file, $path, $url, $type)
- {
- $user = $this->getAuthenticatedUser();
- $userType = $this->getUserType();
- if (!$user) {
- throw new \Exception('User not authenticated');
- }
- // 创建文件记录
- $fileRecord = \App\Models\File::create([
- 'user_id' => $user->id,
- 'user_type' => $userType,
- 'original_name' => $file->getClientOriginalName(),
- 'path' => $path,
- 'url' => $url,
- 'type' => $type,
- 'mime_type' => $file->getMimeType(),
- 'size' => $file->getSize(),
- 'disk' => $this->getDisk(),
- 'ip_address' => $request->ip(),
- 'user_agent' => $request->userAgent(),
- ]);
- return $fileRecord;
- }
- /**
- * 删除文件
- */
- public function delete(Request $request)
- {
- $request->validate([
- 'path' => 'required|string',
- ]);
- $path = $request->input('path');
- $user = $this->getAuthenticatedUser();
- $userType = $this->getUserType();
- // 验证用户权限
- if ($userType !== 'admin') {
- $fileRecord = \App\Models\File::where('path', $path)
- ->where('user_id', $user->id)
- ->where('user_type', $userType)
- ->first();
-
- if (!$fileRecord) {
- return response()->json([
- 'code' => 403,
- 'message' => '文件不存在或权限不足',
- 'data' => null
- ], 403);
- }
- }
- try {
- if (Storage::disk($this->getDisk())->exists($path)) {
- Storage::disk($this->getDisk())->delete($path);
-
- // 删除数据库记录
- \App\Models\File::where('path', $path)->delete();
-
- return response()->json([
- 'code' => 200,
- 'message' => '文件删除成功',
- 'data' => null
- ]);
- }
-
- return response()->json([
- 'code' => 404,
- 'message' => '文件不存在',
- 'data' => null
- ], 404);
-
- } catch (\Exception $e) {
- return response()->json([
- 'code' => 500,
- 'message' => '删除失败: ' . $e->getMessage(),
- 'data' => null
- ], 500);
- }
- }
- /**
- * 获取文件列表(管理员用)
- */
- public function list(Request $request)
- {
- // 验证管理员权限
- if ($this->getUserType() !== 'admin') {
- return response()->json([
- 'code' => 403,
- 'message' => '需要管理员权限',
- 'data' => null
- ], 403);
- }
- $query = \App\Models\File::query();
-
- // 过滤条件
- if ($type = $request->input('type')) {
- $query->where('type', $type);
- }
-
- if ($userType = $request->input('user_type')) {
- $query->where('user_type', $userType);
- }
-
- if ($userId = $request->input('user_id')) {
- $query->where('user_id', $userId);
- }
-
- if ($startDate = $request->input('start_date')) {
- $query->where('created_at', '>=', $startDate);
- }
-
- if ($endDate = $request->input('end_date')) {
- $query->where('created_at', '<=', $endDate);
- }
- $perPage = $request->input('per_page', 20);
- $files = $query->orderBy('created_at', 'desc')->paginate($perPage);
- return response()->json([
- 'code' => 200,
- 'message' => '获取成功',
- 'data' => $files
- ]);
- }
- /**
- * 上传单个文件
- */
- public function upload(Request $request)
- {
- $request->validate([
- 'file' => 'required|file',
- 'type' => 'in:image,document,video,audio,other',
- 'folder' => 'nullable|string|max:100',
- ]);
- $file = $request->file('file');
- $type = $request->input('type', $this->getFileType($file));
- $folder = $request->input('folder', 'uploads');
-
- // 验证文件类型和大小
- $validation = $this->validateFile($file, $type);
- if (!$validation['valid']) {
- return response()->json([
- 'code' => 422,
- 'message' => $validation['message'],
- 'data' => null
- ], 422);
- }
- // 生成文件名
- $extension = $file->getClientOriginalExtension();
- $fileName = Str::random(40) . '.' . $extension;
-
- // 生成路径
- $path = $this->generatePath($folder, $type);
- $fullPath = $path . '/' . $fileName;
- // 存储文件
- try {
- // 如果是图片,可以压缩处理
- if (in_array($extension, $this->allowedMimes['image'])) {
- $this->handleImageUpload($file, $fullPath);
- } else {
- Storage::disk($this->getDisk())->putFileAs($path, $file, $fileName);
- }
- // 获取文件URL
- $url = Storage::disk($this->getDisk())->url($fullPath);
- // 保存到数据库(如果需要)
- $fileRecord = $this->saveFileRecord($request, $file, $fullPath, $url, $type);
- return $this->success(
- [
- // 'url' => $url,
- 'path' => $fullPath,
- 'full_url' => full_url($fullPath),
- 'name' => $file->getClientOriginalName(),
- 'size' => $file->getSize(),
- 'mime_type' => $file->getMimeType(),
- 'id' => $fileRecord->id ?? null,
- ],
- '上传成功'
- );
- } catch (\Exception $e) {
- return response()->json([
- 'code' => 500,
- 'message' => '上传失败: ' . $e->getMessage(),
- 'data' => null
- ], 500);
- }
- }
- /**
- * 辅助方法
- */
- private function getFileType($file)
- {
- $extension = strtolower($file->getClientOriginalExtension());
-
- foreach ($this->allowedMimes as $type => $extensions) {
- if (in_array($extension, $extensions)) {
- return $type;
- }
- }
-
- return 'other';
- }
- private function validateFile($file, $type)
- {
- $extension = strtolower($file->getClientOriginalExtension());
-
- // 检查文件类型
- if ($type !== 'other' && !in_array($extension, $this->allowedMimes[$type] ?? [])) {
- return [
- 'valid' => false,
- 'message' => "File type not allowed for $type"
- ];
- }
- // 检查文件大小
- $maxSize = ($this->maxSizes[$type] ?? 5) * 1024 * 1024;
- if ($file->getSize() > $maxSize) {
- return [
- 'valid' => false,
- 'message' => "File size exceeds maximum allowed size for $type"
- ];
- }
- return ['valid' => true, 'message' => ''];
- }
- // private function handleImageUpload($file, $path)
- // {
- // $image = Image::make($file);
-
- // // 限制最大尺寸
- // if ($image->width() > 2000 || $image->height() > 2000) {
- // $image->resize(2000, 2000, function ($constraint) {
- // $constraint->aspectRatio();
- // $constraint->upsize();
- // });
- // }
-
- // // 压缩图片质量
- // $image->save(storage_path('app/public/' . $path), 80);
- // }
- private function handleImageUpload($file, $path)
- {
- $extension = strtolower($file->getClientOriginalExtension());
- $tempPath = $file->getRealPath();
-
- // 获取图片信息
- $imageInfo = getimagesize($tempPath);
- if (!$imageInfo) {
- throw new \Exception('Invalid image file');
- }
-
- // 根据图片类型创建资源
- switch ($imageInfo[2]) {
- case IMAGETYPE_JPEG:
- $sourceImage = imagecreatefromjpeg($tempPath);
- break;
- case IMAGETYPE_PNG:
- $sourceImage = imagecreatefrompng($tempPath);
- // 保留透明度
- imagealphablending($sourceImage, false);
- imagesavealpha($sourceImage, true);
- break;
- case IMAGETYPE_GIF:
- $sourceImage = imagecreatefromgif($tempPath);
- break;
- case IMAGETYPE_WEBP:
- if (function_exists('imagecreatefromwebp')) {
- $sourceImage = imagecreatefromwebp($tempPath);
- } else {
- // 如果不支持webp,直接存储原文件
- Storage::disk($this->getDisk())->putFileAs(dirname($path), $file, basename($path));
- return;
- }
- break;
- default:
- // 其他格式直接存储
- Storage::disk($this->getDisk())->putFileAs(dirname($path), $file, basename($path));
- return;
- }
-
- // 原始尺寸
- $originalWidth = imagesx($sourceImage);
- $originalHeight = imagesy($sourceImage);
-
- // 新尺寸(保持宽高比)
- $maxWidth = 2000;
- $maxHeight = 2000;
-
- if ($originalWidth <= $maxWidth && $originalHeight <= $maxHeight) {
- // 如果图片小于限制尺寸,不调整大小
- $newWidth = $originalWidth;
- $newHeight = $originalHeight;
- } else {
- // 计算新尺寸
- $ratio = $originalWidth / $originalHeight;
-
- if ($maxWidth / $maxHeight > $ratio) {
- $newWidth = $maxHeight * $ratio;
- $newHeight = $maxHeight;
- } else {
- $newWidth = $maxWidth;
- $newHeight = $maxWidth / $ratio;
- }
-
- $newWidth = (int) $newWidth;
- $newHeight = (int) $newHeight;
-
- // 创建新图片
- $newImage = imagecreatetruecolor($newWidth, $newHeight);
-
- // 处理PNG透明度
- if ($imageInfo[2] === IMAGETYPE_PNG) {
- imagealphablending($newImage, false);
- imagesavealpha($newImage, true);
- $transparent = imagecolorallocatealpha($newImage, 255, 255, 255, 127);
- imagefilledrectangle($newImage, 0, 0, $newWidth, $newHeight, $transparent);
- }
-
- // 调整图片大小
- imagecopyresampled($newImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $originalWidth, $originalHeight);
- imagedestroy($sourceImage);
- $sourceImage = $newImage;
- }
-
- // 保存到临时文件
- $tempFile = tempnam(sys_get_temp_dir(), 'img_');
-
- switch ($extension) {
- case 'jpg':
- case 'jpeg':
- imagejpeg($sourceImage, $tempFile, 80); // 80% 质量
- break;
- case 'png':
- imagepng($sourceImage, $tempFile, 8); // 压缩级别 8
- break;
- case 'gif':
- imagegif($sourceImage, $tempFile);
- break;
- case 'webp':
- if (function_exists('imagewebp')) {
- imagewebp($sourceImage, $tempFile, 80);
- } else {
- imagejpeg($sourceImage, $tempFile, 80);
- }
- break;
- }
-
- imagedestroy($sourceImage);
-
- // 保存到存储
- $content = file_get_contents($tempFile);
- Storage::disk($this->getDisk())->put($path, $content);
-
- // 清理临时文件
- unlink($tempFile);
- }
-
- private function generatePath($folder, $type)
- {
- $date = date('Y/m/d');
- return "{$folder}/{$type}/{$date}";
- }
- private function getDisk()
- {
- return config('filesystems.default', 'public');
- }
- public function getImageUrl()
- {
- return config('app.image_url', null);
- }
-
- }
|