1 <?php namespace BookStack\Services;
4 use BookStack\Exceptions\FileUploadException;
7 use Illuminate\Contracts\Filesystem\FileNotFoundException;
8 use Illuminate\Support\Collection;
9 use Symfony\Component\HttpFoundation\File\UploadedFile;
11 class FileService extends UploadService
15 * Get a file from storage.
19 public function getFile(File $file)
21 $filePath = $this->getStorageBasePath() . $file->path;
22 return $this->getStorage()->get($filePath);
26 * Store a new file upon user upload.
27 * @param UploadedFile $uploadedFile
30 * @throws FileUploadException
32 public function saveNewUpload(UploadedFile $uploadedFile, $page_id)
34 $fileName = $uploadedFile->getClientOriginalName();
35 $filePath = $this->putFileInStorage($fileName, $uploadedFile);
36 $largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
38 $file = File::forceCreate([
41 'uploaded_to' => $page_id,
42 'created_by' => user()->id,
43 'updated_by' => user()->id,
44 'order' => $largestExistingOrder + 1
51 * Store a upload, saving to a file and deleting any existing uploads
52 * attached to that file.
53 * @param UploadedFile $uploadedFile
56 * @throws FileUploadException
58 public function saveUpdatedUpload(UploadedFile $uploadedFile, File $file)
60 if (!$file->external) {
61 $this->deleteFileInStorage($file);
64 $fileName = $uploadedFile->getClientOriginalName();
65 $filePath = $this->putFileInStorage($fileName, $uploadedFile);
67 $file->name = $fileName;
68 $file->path = $filePath;
69 $file->external = false;
75 * Save a new File attachment from a given link and name.
81 public function saveNewFromLink($name, $link, $page_id)
83 $largestExistingOrder = File::where('uploaded_to', '=', $page_id)->max('order');
84 return File::forceCreate([
88 'uploaded_to' => $page_id,
89 'created_by' => user()->id,
90 'updated_by' => user()->id,
91 'order' => $largestExistingOrder + 1
96 * Get the file storage base path, amended for storage type.
97 * This allows us to keep a generic path in the database.
100 private function getStorageBasePath()
102 return $this->isLocal() ? 'storage/' : '';
106 * Updates the file ordering for a listing of attached files.
107 * @param array $fileList
110 public function updateFileOrderWithinPage($fileList, $pageId)
112 foreach ($fileList as $index => $file) {
113 File::where('uploaded_to', '=', $pageId)->where('id', '=', $file['id'])->update(['order' => $index]);
119 * Update the details of a file.
121 * @param $requestData
124 public function updateFile(File $file, $requestData)
126 $file->name = $requestData['name'];
127 if (isset($requestData['link']) && trim($requestData['link']) !== '') {
128 $file->path = $requestData['link'];
129 if (!$file->external) {
130 $this->deleteFileInStorage($file);
131 $file->external = true;
139 * Delete a File from the database and storage.
142 public function deleteFile(File $file)
144 if ($file->external) {
149 $this->deleteFileInStorage($file);
154 * Delete a file from the filesystem it sits on.
155 * Cleans any empty leftover folders.
158 protected function deleteFileInStorage(File $file)
160 $storedFilePath = $this->getStorageBasePath() . $file->path;
161 $storage = $this->getStorage();
162 $dirPath = dirname($storedFilePath);
164 $storage->delete($storedFilePath);
165 if (count($storage->allFiles($dirPath)) === 0) {
166 $storage->deleteDirectory($dirPath);
171 * Store a file in storage with the given filename
173 * @param UploadedFile $uploadedFile
175 * @throws FileUploadException
177 protected function putFileInStorage($fileName, UploadedFile $uploadedFile)
179 $fileData = file_get_contents($uploadedFile->getRealPath());
181 $storage = $this->getStorage();
182 $fileBasePath = 'uploads/files/' . Date('Y-m-M') . '/';
183 $storageBasePath = $this->getStorageBasePath() . $fileBasePath;
185 $uploadFileName = $fileName;
186 while ($storage->exists($storageBasePath . $uploadFileName)) {
187 $uploadFileName = str_random(3) . $uploadFileName;
190 $filePath = $fileBasePath . $uploadFileName;
191 $fileStoragePath = $this->getStorageBasePath() . $filePath;
194 $storage->put($fileStoragePath, $fileData);
195 } catch (Exception $e) {
196 throw new FileUploadException('File path ' . $fileStoragePath . ' could not be uploaded to. Ensure it is writable to the server.');