| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- <?php
- namespace App\Services;
- use App\Models\File;
- use Exception;
- use Illuminate\Support\Facades\Storage;
- use Illuminate\Support\Str;
- use RecursiveIteratorIterator;
- use RuntimeException;
- use ZipArchive;
- class FileService
- {
- /**
- * @param $path
- * @param $file
- * @return File
- */
- public function saveUploadedFile($path, $file): File
- {
- $originalName = $this->sanitizeOriginalName((string)$file->getClientOriginalName());
- $storedFilename = $this->buildStoredFilename($originalName);
- $relativePath = $this->buildUniquePath($path, $storedFilename);
- try {
- Storage::disk('public')->put($relativePath, $file->getContent());
- } catch (\Throwable $e) {
- throw new RuntimeException('Не удалось сохранить файл. Проверьте имя файла и повторите попытку.', 0, $e);
- }
- return File::query()->create([
- 'link' => url('/storage/' . $relativePath),
- 'path' => $relativePath,
- 'user_id' => auth()->user()->id,
- 'original_name' => $originalName,
- 'mime_type' => $file->getClientMimeType(),
- ]);
- }
- private function sanitizeOriginalName(string $originalName): string
- {
- $name = basename(str_replace('\\', '/', $originalName));
- if (class_exists('\Normalizer')) {
- $normalized = \Normalizer::normalize($name, \Normalizer::FORM_C);
- if (is_string($normalized)) {
- $name = $normalized;
- }
- }
- // Remove control/format unicode symbols (e.g. zero-width, LRM/RLM), then normalize spaces.
- $name = preg_replace('/\p{C}+/u', '', $name) ?? '';
- $name = preg_replace('/\p{Z}+/u', ' ', $name) ?? '';
- $name = trim($name);
- return $name === '' ? 'file' : $name;
- }
- private function buildStoredFilename(string $originalName): string
- {
- $baseName = pathinfo($originalName, PATHINFO_FILENAME);
- $extension = pathinfo($originalName, PATHINFO_EXTENSION);
- $baseName = preg_replace('/[<>:|?*\/\\\\]+/u', '_', $baseName) ?? '';
- $baseName = preg_replace('/\s+/u', ' ', $baseName) ?? '';
- $baseName = trim($baseName, " .\t\n\r\0\x0B");
- if ($baseName === '' || $baseName === '.' || $baseName === '..') {
- $baseName = 'file';
- }
- if (function_exists('mb_substr')) {
- $baseName = mb_substr($baseName, 0, 150);
- } else {
- $baseName = substr($baseName, 0, 150);
- }
- $extension = preg_replace('/[^A-Za-z0-9]+/', '', $extension) ?? '';
- return $extension === '' ? $baseName : $baseName . '.' . $extension;
- }
- private function buildUniquePath(string $directory, string $filename): string
- {
- $directory = trim($directory, '/');
- $basePath = $directory === '' ? $filename : $directory . '/' . $filename;
- if (!Storage::disk('public')->exists($basePath)) {
- return $basePath;
- }
- $baseName = pathinfo($filename, PATHINFO_FILENAME);
- $extension = pathinfo($filename, PATHINFO_EXTENSION);
- for ($index = 1; $index < 10000; $index++) {
- $candidateName = $baseName . ' (' . $index . ')';
- if ($extension !== '') {
- $candidateName .= '.' . $extension;
- }
- $candidatePath = $directory === '' ? $candidateName : $directory . '/' . $candidateName;
- if (!Storage::disk('public')->exists($candidatePath)) {
- return $candidatePath;
- }
- }
- return $basePath;
- }
- /**
- * @param string $path
- * @param string $archiveName
- * @param int|null $userId
- * @return File
- * @throws Exception
- */
- public function createZipArchive(string $path, string $archiveName, int $userId = null): File
- {
- if (!($tempFile = tempnam(sys_get_temp_dir(), Str::random()))) {
- throw new Exception('Cant create temporary file!');
- }
- $zip = new ZipArchive();
- $fullPath = storage_path('app/public/' . $path);
- $zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
- $files = new RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator($fullPath),
- RecursiveIteratorIterator::LEAVES_ONLY
- );
- foreach ($files as $file) {
- $filePath = $file->getRealPath();
- $relativePath = substr($filePath, strlen($fullPath) + 1);
- if(!$file->isDir()) {
- $zip->addFile($filePath, $relativePath);
- } else {
- $zip->addEmptyDir($relativePath);
- }
- }
- $zip->close();
- $fileModel = File::query()->updateOrCreate([
- 'link' => url('/storage/') . '/' . Str::replace('/tmp', '', $path) . '/' .$archiveName,
- 'path' => $path . '/' .$archiveName,
- 'user_id' => $userId ?? auth()->user()->id,
- 'original_name' => $archiveName,
- 'mime_type' => 'application/zip',
- ]);
- $contents = file_get_contents($tempFile);
- Storage::disk('public')->put($path . '/../' .$archiveName, $contents);
- unlink($tempFile);
- return $fileModel;
- }
- }
|