diff --git a/app/Http/Controllers/Api/H5UploadController.php b/app/Http/Controllers/Api/H5UploadController.php index a201711..7f4ca0c 100644 --- a/app/Http/Controllers/Api/H5UploadController.php +++ b/app/Http/Controllers/Api/H5UploadController.php @@ -6,6 +6,8 @@ use App\Http\Controllers\Controller; use App\Models\WechatUser; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Storage; +use Throwable; class H5UploadController extends Controller { @@ -16,15 +18,35 @@ class H5UploadController extends Controller abort(403, '仅微信用户可上传'); } + if (!$request->hasFile('file')) { + return response()->json(['message' => '未收到文件'], 422); + } + + $uploaded = $request->file('file'); + if (!$uploaded->isValid()) { + return response()->json(['message' => $uploaded->getErrorMessage()], 422); + } + $data = $request->validate([ 'file' => ['required', 'file', 'max:5120', 'mimes:jpg,jpeg,png,gif,webp'], ]); - $path = $data['file']->store('uploads/h5', 'public'); - $url = url('/storage/'.$path); + try { + $path = Storage::disk('public')->putFile('uploads/h5', $data['file']); + } catch (Throwable $e) { + report($e); + + return response()->json(['message' => '文件保存失败'], 500); + } + + if ($path === false) { + return response()->json(['message' => '文件保存失败'], 500); + } + + $safePath = str_replace('\\', '/', $path); return response()->json([ - 'url' => $url, + 'url' => url('/storage/'.$safePath), 'path' => $path, ]); } diff --git a/app/Http/Controllers/Api/UploadController.php b/app/Http/Controllers/Api/UploadController.php index fe18901..6888451 100644 --- a/app/Http/Controllers/Api/UploadController.php +++ b/app/Http/Controllers/Api/UploadController.php @@ -5,21 +5,75 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Storage; +use Throwable; class UploadController extends Controller { public function store(Request $request): JsonResponse { + if (!$request->hasFile('file')) { + return response()->json([ + 'message' => '未收到文件,请使用 multipart 表单字段名 file。', + ], 422); + } + + $uploaded = $request->file('file'); + if (!$uploaded->isValid()) { + return response()->json([ + 'message' => '上传未通过校验:'.$uploaded->getErrorMessage(), + ], 422); + } + $data = $request->validate([ 'file' => ['required', 'file', 'max:20480'], ]); - $path = $data['file']->store('uploads', 'public'); + try { + $path = Storage::disk('public')->putFile('uploads', $data['file']); + } catch (Throwable $e) { + report($e); + + return response()->json([ + 'message' => '文件保存失败', + ], 500); + } + + if ($path === false) { + return response()->json([ + 'message' => '文件保存失败', + ], 500); + } + + try { + $mime = $data['file']->getMimeType() + ?: $data['file']->getClientMimeType() + ?: 'application/octet-stream'; + } catch (Throwable) { + $mime = 'application/octet-stream'; + } + return response()->json([ 'path' => $path, - 'url' => url('/storage/' . $path), - 'mime' => $data['file']->getClientMimeType(), + 'url' => url('/storage/'.str_replace('\\', '/', $path)), + 'mime' => self::jsonSafeString($mime), 'size' => $data['file']->getSize(), ]); } + + /** 避免 mime 等字段含非法 UTF-8 导致 json_encode 抛错成 500 */ + private static function jsonSafeString(string $value): string + { + if ($value === '') { + return ''; + } + + if (function_exists('mb_scrub')) { + return mb_scrub($value, 'UTF-8'); + } + + $clean = @iconv('UTF-8', 'UTF-8//IGNORE', $value); + + return $clean !== false ? $clean : 'application/octet-stream'; + } } diff --git a/app/Http/Middleware/AuditLogMiddleware.php b/app/Http/Middleware/AuditLogMiddleware.php index 595790e..7ac816f 100644 --- a/app/Http/Middleware/AuditLogMiddleware.php +++ b/app/Http/Middleware/AuditLogMiddleware.php @@ -54,8 +54,13 @@ class AuditLogMiddleware return false; } - // multipart 上传不参与审计,避免大 payload 与序列化问题 - if ($request->is('api/upload') || $request->is('api/h5/upload')) { + // 任意 multipart 文件请求不参与审计(避免路径前缀不一致时仍序列化 UploadedFile) + if ($request->files->count() > 0) { + return false; + } + + // 仍按路由名兜底(无文件字段但走上传 URL 的极端情况) + if ($request->is('api/upload') || $request->is('api/h5/upload') || $request->is('*/api/upload') || $request->is('*/api/h5/upload')) { return false; }