• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nepada / file-upload-control / 6449927556

08 Oct 2023 08:55PM UTC coverage: 90.363% (+0.05%) from 90.309%
6449927556

push

github

xificurk
Drop useless dev dependency

647 of 716 relevant lines covered (90.36%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.74
/src/FileUploadControl/Storage/FileSystemStorage.php
1
<?php
2
declare(strict_types = 1);
3

4
namespace Nepada\FileUploadControl\Storage;
5

6
use Nepada\FileUploadControl\Storage\Metadata\FileUploadMetadata;
7
use Nepada\FileUploadControl\Storage\Metadata\FileUploadMetadataAlreadyExistsException;
8
use Nepada\FileUploadControl\Storage\Metadata\FileUploadMetadataNotFoundException;
9
use Nepada\FileUploadControl\Storage\Metadata\MetadataJournal;
10
use Nepada\FileUploadControl\Utils\FileSystem;
11
use Nette;
12
use Nette\Http\FileUpload;
13

14
final class FileSystemStorage implements Storage
15
{
16

17
    use Nette\SmartObject;
18

19
    private MetadataJournal $metadataJournal;
20

21
    private FileSystem $fileSystem;
22

23
    private string $directory;
24

25
    public function __construct(MetadataJournal $metadataJournal, FileSystem $fileSystem, string $directory)
1✔
26
    {
27
        $this->metadataJournal = $metadataJournal;
1✔
28
        $this->fileSystem = $fileSystem;
1✔
29
        if (! $this->fileSystem->directoryExists($directory)) {
1✔
30
            throw new \InvalidArgumentException("Directory '$directory' does not exist.");
×
31
        }
32
        if (! $this->fileSystem->isWritable($directory)) {
1✔
33
            throw new \InvalidArgumentException("Directory '$directory' is not writable.");
×
34
        }
35
        $this->directory = $directory;
1✔
36
    }
1✔
37

38
    /**
39
     * @return FileUploadItem[]
40
     */
41
    public function list(): array
42
    {
43
        $items = [];
1✔
44
        foreach ($this->metadataJournal->list() as $id) {
1✔
45
            try {
46
                $fileUploadItem = $this->load($id);
1✔
47
            } catch (FileUploadNotFoundException $exception) {
1✔
48
                continue;
1✔
49
            }
50
            $items[] = $fileUploadItem;
1✔
51
        }
52
        return $items;
1✔
53
    }
54

55
    /**
56
     * @throws FileUploadNotFoundException
57
     */
58
    public function load(FileUploadId $id): FileUploadItem
1✔
59
    {
60
        try {
61
            $metadata = $this->metadataJournal->load($id);
1✔
62
        } catch (FileUploadMetadataNotFoundException $exception) {
1✔
63
            throw new FileUploadNotFoundException($exception->getMessage(), 0, $exception);
1✔
64
        }
65

66
        $file = $this->getFilePath($id);
1✔
67
        if (! $this->fileSystem->fileExists($file)) {
1✔
68
            throw FileUploadNotFoundException::withId($id);
1✔
69
        }
70

71
        $error = $this->fileSystem->fileSize($file) === $metadata->getSize() ? UPLOAD_ERR_OK : UPLOAD_ERR_PARTIAL;
1✔
72

73
        $fileUpload = new FileUpload([
1✔
74
            'name' => $metadata->getName(),
1✔
75
            'size' => $metadata->getSize(),
1✔
76
            'tmp_name' => $file,
1✔
77
            'error' => $error,
1✔
78
        ]);
79
        return new FileUploadItem($id, $fileUpload);
1✔
80
    }
81

82
    public function delete(FileUploadId $id): void
1✔
83
    {
84
        $this->metadataJournal->delete($id);
1✔
85
        $this->fileSystem->delete($this->getFilePath($id));
1✔
86
    }
1✔
87

88
    /**
89
     * @throws UnableToSaveFileUploadException
90
     */
91
    public function save(FileUploadChunk $fileUploadChunk): FileUploadItem
1✔
92
    {
93
        if (! $fileUploadChunk->getFileUpload()->isOk()) {
1✔
94
            throw UnableToSaveFileUploadException::withUploadError();
1✔
95
        }
96

97
        if ($fileUploadChunk->getContentRange()->containsFirstByte()) {
1✔
98
            $id = $this->saveNewUpload($fileUploadChunk);
1✔
99
        } else {
100
            $id = $this->resumeExistingUpload($fileUploadChunk);
1✔
101
        }
102

103
        try {
104
            return $this->load($id);
1✔
105
        } catch (FileUploadNotFoundException $exception) {
×
106
            throw new UnableToSaveFileUploadException($exception->getMessage(), 0, $exception);
×
107
        }
108
    }
109

110
    public function destroy(): void
111
    {
112
        $this->metadataJournal->destroy();
1✔
113
        $this->fileSystem->delete($this->directory);
1✔
114
    }
1✔
115

116
    private function getFilePath(FileUploadId $id): string
1✔
117
    {
118
        return $this->directory . DIRECTORY_SEPARATOR . $id->toString();
1✔
119
    }
120

121
    /**
122
     * @throws UnableToSaveFileUploadException
123
     */
124
    private function saveNewUpload(FileUploadChunk $fileUploadChunk): FileUploadId
1✔
125
    {
126
        $metadata = FileUploadMetadata::fromFileUploadChunk($fileUploadChunk);
1✔
127
        $id = $metadata->createFileUploadId();
1✔
128
        try {
129
            $this->metadataJournal->save($id, $metadata);
1✔
130
        } catch (FileUploadMetadataAlreadyExistsException $exception) {
1✔
131
            throw UnableToSaveFileUploadException::withConflict($id);
1✔
132
        }
133

134
        $file = $this->getFilePath($id);
1✔
135
        $contents = $fileUploadChunk->getFileUpload()->getContents();
1✔
136
        assert(is_string($contents));
1✔
137
        $this->fileSystem->write($file, $contents);
1✔
138

139
        return $id;
1✔
140
    }
141

142
    /**
143
     * @throws UnableToSaveFileUploadException
144
     */
145
    private function resumeExistingUpload(FileUploadChunk $fileUploadChunk): FileUploadId
1✔
146
    {
147
        $metadata = FileUploadMetadata::fromFileUploadChunk($fileUploadChunk);
1✔
148
        $id = $metadata->createFileUploadId();
1✔
149
        try {
150
            $storedMetadata = $this->metadataJournal->load($id);
1✔
151
            if (! $metadata->equals($storedMetadata)) {
1✔
152
                throw UnableToSaveFileUploadException::withConflict($id);
1✔
153
            }
154
        } catch (FileUploadMetadataNotFoundException $exception) {
1✔
155
            throw UnableToSaveFileUploadException::withFailedChunk($id, 'failed to load metadata');
1✔
156
        }
157

158
        $file = $this->getFilePath($id);
1✔
159
        if (! $this->fileSystem->fileExists($file)) {
1✔
160
            throw UnableToSaveFileUploadException::withFailedChunk($id, 'missing previously uploaded file part');
1✔
161
        }
162
        if ($this->fileSystem->fileSize($file) !== $fileUploadChunk->getContentRange()->getStart()) {
1✔
163
            throw UnableToSaveFileUploadException::withFailedChunk($id, 'previously uploaded file part size does not match given content-range value');
1✔
164
        }
165

166
        $contents = $fileUploadChunk->getFileUpload()->getContents();
1✔
167
        assert(is_string($contents));
1✔
168
        $this->fileSystem->append($file, $contents);
1✔
169

170
        return $id;
1✔
171
    }
172

173
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc