-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Expand file tree
/
Copy pathImageStorageDisk.php
More file actions
150 lines (129 loc) · 4.61 KB
/
ImageStorageDisk.php
File metadata and controls
150 lines (129 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?php
namespace BookStack\Uploads;
use BookStack\Util\FilePathNormalizer;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Filesystem\FilesystemAdapter;
use Symfony\Component\HttpFoundation\StreamedResponse;
class ImageStorageDisk
{
public function __construct(
protected string $diskName,
protected Filesystem $filesystem,
) {
}
/**
* Check if local secure image storage (Fetched behind authentication)
* is currently active in the instance.
*/
public function usingSecureImages(): bool
{
return $this->diskName === 'local_secure_images';
}
/**
* Change the originally provided path to fit any disk-specific requirements.
* This also ensures the path is kept to the expected root folders.
*/
protected function adjustPathForDisk(string $path): string
{
$trimmed = str_replace('uploads/images/', '', $path);
$normalized = FilePathNormalizer::normalize($trimmed);
if ($this->usingSecureImages()) {
return $normalized;
}
return 'uploads/images/' . $normalized;
}
/**
* Check if a file at the given path exists.
*/
public function exists(string $path): bool
{
return $this->filesystem->exists($this->adjustPathForDisk($path));
}
/**
* Get the file at the given path.
*/
public function get(string $path): ?string
{
return $this->filesystem->get($this->adjustPathForDisk($path));
}
/**
* Get a stream to the file at the given path.
* @returns ?resource
*/
public function stream(string $path): mixed
{
return $this->filesystem->readStream($this->adjustPathForDisk($path));
}
/**
* Save the given image data at the given path. Can choose to set
* the image as public which will update its visibility after saving.
*/
public function put(string $path, string $data, bool $makePublic = false): void
{
$path = $this->adjustPathForDisk($path);
$this->filesystem->put($path, $data);
// Set visibility when a non-AWS-s3, s3-like storage option is in use.
// Done since this call can break s3-like services but desired for other image stores.
// Attempting to set ACL during above put request requires different permissions
// hence would technically be a breaking change for actual s3 usage.
if ($makePublic && !$this->isS3Like()) {
$this->filesystem->setVisibility($path, 'public');
}
}
/**
* Destroys an image at the given path.
* Searches for image thumbnails in addition to main provided path.
*/
public function destroyAllMatchingNameFromPath(string $path): void
{
$path = $this->adjustPathForDisk($path);
$imageFolder = dirname($path);
$imageFileName = basename($path);
$allImages = collect($this->filesystem->allFiles($imageFolder));
// Delete image files
$imagesToDelete = $allImages->filter(function ($imagePath) use ($imageFileName) {
return basename($imagePath) === $imageFileName;
});
$this->filesystem->delete($imagesToDelete->all());
// Cleanup of empty folders
$foldersInvolved = array_merge([$imageFolder], $this->filesystem->directories($imageFolder));
foreach ($foldersInvolved as $directory) {
if ($this->isFolderEmpty($directory)) {
$this->filesystem->deleteDirectory($directory);
}
}
}
/**
* Get the mime type of the file at the given path.
* Only works for local filesystem adapters.
*/
public function mimeType(string $path): string
{
$path = $this->adjustPathForDisk($path);
return $this->filesystem instanceof FilesystemAdapter ? $this->filesystem->mimeType($path) : '';
}
/**
* Get a stream response for the image at the given path.
*/
public function response(string $path): StreamedResponse
{
return $this->filesystem->response($this->adjustPathForDisk($path));
}
/**
* Check if the image storage in use is an S3-like (but not likely S3) external system.
*/
protected function isS3Like(): bool
{
$usingS3 = $this->diskName === 's3';
return $usingS3 && !is_null(config('filesystems.disks.s3.endpoint'));
}
/**
* Check whether a folder is empty.
*/
protected function isFolderEmpty(string $path): bool
{
$files = $this->filesystem->files($path);
$folders = $this->filesystem->directories($path);
return count($files) === 0 && count($folders) === 0;
}
}