3 namespace BookStack\Exports\ZipExports;
5 use BookStack\Uploads\Attachment;
6 use BookStack\Uploads\AttachmentService;
7 use BookStack\Uploads\Image;
8 use BookStack\Uploads\ImageService;
9 use Illuminate\Support\Str;
14 * References for attachments by attachment ID.
15 * @var array<int, string>
17 protected array $attachmentRefsById = [];
20 * References for images by image ID.
21 * @var array<int, string>
23 protected array $imageRefsById = [];
25 public function __construct(
26 protected AttachmentService $attachmentService,
27 protected ImageService $imageService,
32 * Gain a reference to the given attachment instance.
33 * This is expected to be a file-based attachment that the user
34 * has visibility of, no permission/access checks are performed here.
36 public function referenceForAttachment(Attachment $attachment): string
38 if (isset($this->attachmentRefsById[$attachment->id])) {
39 return $this->attachmentRefsById[$attachment->id];
42 $existingFiles = $this->getAllFileNames();
44 $fileName = Str::random(20) . '.' . $attachment->extension;
45 } while (in_array($fileName, $existingFiles));
47 $this->attachmentRefsById[$attachment->id] = $fileName;
53 * Gain a reference to the given image instance.
54 * This is expected to be an image that the user has visibility of,
55 * no permission/access checks are performed here.
57 public function referenceForImage(Image $image): string
59 if (isset($this->imageRefsById[$image->id])) {
60 return $this->imageRefsById[$image->id];
63 $existingFiles = $this->getAllFileNames();
64 $extension = pathinfo($image->path, PATHINFO_EXTENSION);
66 $fileName = Str::random(20) . '.' . $extension;
67 } while (in_array($fileName, $existingFiles));
69 $this->imageRefsById[$image->id] = $fileName;
74 protected function getAllFileNames(): array
77 array_values($this->attachmentRefsById),
78 array_values($this->imageRefsById),
83 * Extract each of the ZIP export tracked files.
84 * Calls the given callback for each tracked file, passing a temporary
85 * file reference of the file contents, and the zip-local tracked reference.
87 public function extractEach(callable $callback): void
89 foreach ($this->attachmentRefsById as $attachmentId => $ref) {
90 $attachment = Attachment::query()->find($attachmentId);
91 $stream = $this->attachmentService->streamAttachmentFromStorage($attachment);
92 $tmpFile = tempnam(sys_get_temp_dir(), 'bszipfile-');
93 $tmpFileStream = fopen($tmpFile, 'w');
94 stream_copy_to_stream($stream, $tmpFileStream);
95 $callback($tmpFile, $ref);
98 foreach ($this->imageRefsById as $imageId => $ref) {
99 $image = Image::query()->find($imageId);
100 $stream = $this->imageService->getImageStream($image);
101 $tmpFile = tempnam(sys_get_temp_dir(), 'bszipimage-');
102 $tmpFileStream = fopen($tmpFile, 'w');
103 stream_copy_to_stream($stream, $tmpFileStream);
104 $callback($tmpFile, $ref);