file($inputName); if (!$file || !$file->isValid()) { throw new \RuntimeException('上传文件无效'); } $extension = strtolower($file->getUploadExtension() ?: ''); $fileType = $this->detectFileType($extension); if ($fileType === 'file') { throw new \RuntimeException('仅支持上传图片、视频或 PDF 文件'); } $filename = sprintf('evidence_%s.%s', uniqid(), $extension ?: 'dat'); $relativeDir = 'uploads/appraisal-evidence/' . date('Ymd'); $relativePath = $relativeDir . '/' . $filename; $this->storage()->putUploadedFile($file, $relativePath); $fileUrl = $this->storage()->publicUrl($request, $relativePath); return [ 'file_id' => md5($relativePath), 'file_url' => $fileUrl, 'thumbnail_url' => $fileType === 'image' ? $fileUrl : '', 'name' => $file->getUploadName(), 'file_type' => $fileType, 'mime_type' => $this->mimeType($fileType, $extension), ]; } public function delete(string $fileUrl): void { $relativePath = $this->storage()->storagePath($fileUrl); if (!str_starts_with($relativePath, 'uploads/appraisal-evidence/')) { return; } $this->storage()->delete($relativePath); } public function normalize(mixed $attachments, ?Request $request = null, bool $forStorage = false): array { if (is_string($attachments) && $attachments !== '') { $decoded = json_decode($attachments, true); $attachments = is_array($decoded) ? $decoded : []; } if (!is_array($attachments)) { return []; } $list = []; foreach ($attachments as $item) { if (!is_array($item)) { continue; } $fileUrl = trim((string)($item['file_url'] ?? '')); if ($fileUrl === '') { continue; } $name = trim((string)($item['name'] ?? '')); $mimeType = trim((string)($item['mime_type'] ?? '')); $fileType = trim((string)($item['file_type'] ?? '')); if ($fileType === '') { $path = parse_url('/' . $this->storage()->storagePath($fileUrl), PHP_URL_PATH) ?: ''; $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); $fileType = $this->detectFileType($extension); if ($mimeType === '') { $mimeType = $this->mimeType($fileType, $extension); } } $storedFileUrl = $this->storage()->storagePath($fileUrl); $storedThumbnailUrl = $this->storage()->storagePath(trim((string)($item['thumbnail_url'] ?? ($fileType === 'image' ? $fileUrl : '')))); $list[] = [ 'file_id' => trim((string)($item['file_id'] ?? md5($storedFileUrl))), 'file_url' => $forStorage ? '/' . $storedFileUrl : ($request ? $this->storage()->normalizeUrl($fileUrl, $request) : $fileUrl), 'thumbnail_url' => $forStorage ? ($storedThumbnailUrl !== '' ? '/' . $storedThumbnailUrl : '') : ($request ? $this->storage()->normalizeUrl(trim((string)($item['thumbnail_url'] ?? ($fileType === 'image' ? $fileUrl : ''))), $request) : trim((string)($item['thumbnail_url'] ?? ($fileType === 'image' ? $fileUrl : '')))), 'name' => $name, 'file_type' => $fileType ?: 'file', 'mime_type' => $mimeType, ]; } return $list; } public function detectFileType(string $extension): string { if (in_array($extension, self::IMAGE_EXTENSIONS, true)) { return 'image'; } if (in_array($extension, self::VIDEO_EXTENSIONS, true)) { return 'video'; } if (in_array($extension, self::PDF_EXTENSIONS, true)) { return 'pdf'; } return 'file'; } private function mimeType(string $fileType, string $extension): string { return match ($fileType) { 'image' => 'image/' . ($extension === 'jpg' ? 'jpeg' : ($extension ?: 'jpeg')), 'video' => 'video/' . ($extension ?: 'mp4'), 'pdf' => 'application/pdf', default => 'application/octet-stream', }; } private function storage(): FileStorageService { return new FileStorageService(); } }