FileService.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <?php
  2. namespace App\Services;
  3. use App\Models\File;
  4. use Exception;
  5. use Illuminate\Support\Facades\Storage;
  6. use Illuminate\Support\Str;
  7. use RecursiveIteratorIterator;
  8. use ZipArchive;
  9. class FileService
  10. {
  11. /**
  12. * @param $path
  13. * @param $file
  14. * @return File
  15. */
  16. public function saveUploadedFile($path, $file): File
  17. {
  18. $originalName = $this->sanitizeOriginalName((string)$file->getClientOriginalName());
  19. $storedFilename = $this->buildStoredFilename($originalName);
  20. $relativePath = $this->buildUniquePath($path, $storedFilename);
  21. Storage::disk('public')->put($relativePath, $file->getContent());
  22. return File::query()->create([
  23. 'link' => url('/storage/' . $relativePath),
  24. 'path' => $relativePath,
  25. 'user_id' => auth()->user()->id,
  26. 'original_name' => $originalName,
  27. 'mime_type' => $file->getClientMimeType(),
  28. ]);
  29. }
  30. private function sanitizeOriginalName(string $originalName): string
  31. {
  32. $name = basename(str_replace('\\', '/', $originalName));
  33. $name = preg_replace('/[\x00-\x1F\x7F]+/u', '', $name) ?? '';
  34. $name = trim($name);
  35. return $name === '' ? 'file' : $name;
  36. }
  37. private function buildStoredFilename(string $originalName): string
  38. {
  39. $baseName = pathinfo($originalName, PATHINFO_FILENAME);
  40. $extension = pathinfo($originalName, PATHINFO_EXTENSION);
  41. $baseName = preg_replace('/[<>:|?*\/\\\\]+/u', '_', $baseName) ?? '';
  42. $baseName = preg_replace('/\s+/u', ' ', $baseName) ?? '';
  43. $baseName = trim($baseName, " .\t\n\r\0\x0B");
  44. if ($baseName === '' || $baseName === '.' || $baseName === '..') {
  45. $baseName = 'file';
  46. }
  47. if (function_exists('mb_substr')) {
  48. $baseName = mb_substr($baseName, 0, 150);
  49. } else {
  50. $baseName = substr($baseName, 0, 150);
  51. }
  52. $extension = preg_replace('/[^A-Za-z0-9]+/', '', $extension) ?? '';
  53. return $extension === '' ? $baseName : $baseName . '.' . $extension;
  54. }
  55. private function buildUniquePath(string $directory, string $filename): string
  56. {
  57. $directory = trim($directory, '/');
  58. $basePath = $directory === '' ? $filename : $directory . '/' . $filename;
  59. if (!Storage::disk('public')->exists($basePath)) {
  60. return $basePath;
  61. }
  62. $baseName = pathinfo($filename, PATHINFO_FILENAME);
  63. $extension = pathinfo($filename, PATHINFO_EXTENSION);
  64. for ($index = 1; $index < 10000; $index++) {
  65. $candidateName = $baseName . ' (' . $index . ')';
  66. if ($extension !== '') {
  67. $candidateName .= '.' . $extension;
  68. }
  69. $candidatePath = $directory === '' ? $candidateName : $directory . '/' . $candidateName;
  70. if (!Storage::disk('public')->exists($candidatePath)) {
  71. return $candidatePath;
  72. }
  73. }
  74. return $basePath;
  75. }
  76. /**
  77. * @param string $path
  78. * @param string $archiveName
  79. * @param int|null $userId
  80. * @return File
  81. * @throws Exception
  82. */
  83. public function createZipArchive(string $path, string $archiveName, int $userId = null): File
  84. {
  85. if (!($tempFile = tempnam(sys_get_temp_dir(), Str::random()))) {
  86. throw new Exception('Cant create temporary file!');
  87. }
  88. $zip = new ZipArchive();
  89. $fullPath = storage_path('app/public/' . $path);
  90. $zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
  91. $files = new RecursiveIteratorIterator(
  92. new \RecursiveDirectoryIterator($fullPath),
  93. RecursiveIteratorIterator::LEAVES_ONLY
  94. );
  95. foreach ($files as $file) {
  96. $filePath = $file->getRealPath();
  97. $relativePath = substr($filePath, strlen($fullPath) + 1);
  98. if(!$file->isDir()) {
  99. $zip->addFile($filePath, $relativePath);
  100. } else {
  101. $zip->addEmptyDir($relativePath);
  102. }
  103. }
  104. $zip->close();
  105. $fileModel = File::query()->updateOrCreate([
  106. 'link' => url('/storage/') . '/' . Str::replace('/tmp', '', $path) . '/' .$archiveName,
  107. 'path' => $path . '/' .$archiveName,
  108. 'user_id' => $userId ?? auth()->user()->id,
  109. 'original_name' => $archiveName,
  110. 'mime_type' => 'application/zip',
  111. ]);
  112. $contents = file_get_contents($tempFile);
  113. Storage::disk('public')->put($path . '/../' .$archiveName, $contents);
  114. unlink($tempFile);
  115. return $fileModel;
  116. }
  117. }