ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
FileUploadImpl.php
Go to the documentation of this file.
1<?php
2
3namespace ILIAS\FileUpload;
4
18use Psr\Http\Message\UploadedFileInterface;
19use RecursiveArrayIterator;
20use RecursiveIteratorIterator;
21
29final class FileUploadImpl implements FileUpload
30{
31
39 private $filesystems;
47 private $processed;
51 private $moved;
64
65
66
67
76 {
77 $this->processorManager = $processorManager;
79 $this->globalHttpState = $globalHttpState;
80 $this->processed = false;
81 $this->moved = false;
82 $this->uploadResult = [];
83 $this->rejectedUploadResult = [];
84 }
85
89 public function moveOneFileTo(UploadResult $uploadResult, $destination, $location = Location::STORAGE, $file_name = '', $override_existing = false)
90 {
91 if ($this->processed === false) {
92 throw new \RuntimeException('Can not move unprocessed files.');
93 }
94 $filesystem = $this->selectFilesystem($location);
95 $tempResults = [];
96
97 if ($uploadResult->getStatus()->getCode() == ProcessingStatus::REJECTED) {
98 return false;
99 }
100
101 try {
102 $path = rtrim($destination, "/") . '/' . ($file_name == "" ? $uploadResult->getName() : $file_name);
103 if ($override_existing && $filesystem->has($path)) {
104 $filesystem->delete($path);
105 }
106 $filesystem->writeStream($path, Streams::ofPsr7Stream($this->uploadStreams[$uploadResult->getPath()]));
107 $tempResults[] = $this->regenerateUploadResultWithPath($uploadResult, $path);
108 } catch (IOException $ex) {
109 $this->regenerateUploadResultWithCopyError($uploadResult, $ex->getMessage());
110 }
111 }
112
113
117 public function moveFilesTo($destination, $location = Location::STORAGE)
118 {
119 if ($this->processed === false) {
120 throw new \RuntimeException('Can not move unprocessed files.');
121 }
122
123 if ($this->moved === true) {
124 throw new \RuntimeException('Can not move the files a second time.');
125 }
126
127 $filesystem = $this->selectFilesystem($location);
128 $tempResults = [];
129
130 foreach ($this->uploadResult as $key => $uploadResult) {
131 if ($uploadResult->getStatus()->getCode() == ProcessingStatus::REJECTED) {
132 continue;
133 }
134
135 try {
136 $path = $destination . '/' . $uploadResult->getName();
137 $filesystem->writeStream($path, Streams::ofPsr7Stream($this->uploadStreams[$key]));
138 $tempResults[] = $this->regenerateUploadResultWithPath($uploadResult, $path);
139 } catch (IOException $ex) {
140 $this->regenerateUploadResultWithCopyError($uploadResult, $ex->getMessage());
141 }
142 }
143
144 $this->uploadResult = $tempResults;
145 $this->uploadStreams = null;
146 $this->moved = true;
147 }
148
149
159 {
160 return new UploadResult(
161 $result->getName(),
162 $result->getSize(),
163 $result->getMimeType(),
164 $result->getMetaData(),
165 $result->getStatus(),
166 $path
167 );
168 }
169
170
180 {
181 return new UploadResult(
182 $result->getName(),
183 $result->getSize(),
184 $result->getMimeType(),
185 $result->getMetaData(),
187 ''
188 );
189 }
190
191
203 private function selectFilesystem($location)
204 {
205 switch ($location) {
207 return $this->filesystems->customizing();
209 return $this->filesystems->storage();
210 case Location::WEB:
211 return $this->filesystems->web();
213 return $this->filesystems->temp();
214 default:
215 throw new \InvalidArgumentException("No filesystem found for location code \"$location\"");
216 }
217 }
218
219
223 public function uploadSizeLimit()
224 {
225 return \ilUtil::getUploadSizeLimitBytes();
226 }
227
228
232 public function register(PreProcessor $preProcessor)
233 {
234 if ($this->processed === false) {
235 $this->processorManager->with($preProcessor);
236 } else {
237 throw new IllegalStateException('Can not register processor after the upload was processed.');
238 }
239 }
240
241
245 public function process()
246 {
247 if ($this->processed === true) {
248 throw new IllegalStateException('Can not reprocess the uploaded files.');
249 }
250
254 $uploadedFiles = $this->globalHttpState->request()->getUploadedFiles();
255 $collectFilesFromNestedFields = $this->flattenUploadedFiles($uploadedFiles);
256 foreach ($collectFilesFromNestedFields as $file) {
257 $metadata = new Metadata($file->getClientFilename(), $file->getSize(), $file->getClientMediaType());
258 try {
259 $stream = Streams::ofPsr7Stream($file->getStream());
260 } catch (\RuntimeException $e) {
261 $this->rejectFailedUpload($file, $metadata);
262 continue;
263 }
264
265 // we take the temporary file name as an identifier as it is the only unique attribute.
266 $identifier = $file->getStream()->getMetadata('uri');
267
268 $identifier = is_array($identifier) ? '' : $identifier;
269
270 $this->uploadStreams[$identifier] = $stream;
271
272 if ($file->getError() === UPLOAD_ERR_OK) {
273 $processingResult = $this->processorManager->process($stream, $metadata);
274 $result = new UploadResult(
275 $metadata->getFilename(),
276 $metadata->getUploadSize(),
277 $metadata->getMimeType(),
278 $metadata->additionalMetaData(),
279 $processingResult,
280 is_string($identifier)?$identifier:''
281 );
282 $this->uploadResult[$identifier] = $result;
283 } else {
284 $this->rejectFailedUpload($file, $metadata);
285 }
286 }
287
288 $this->processed = true;
289 }
290
291
300 private function rejectFailedUpload(UploadedFileInterface $file, Metadata $metadata)
301 {
302 //reject failed upload
303 $processingStatus = new ProcessingStatus(ProcessingStatus::REJECTED, 'Upload failed');
304 $extraMetadata = new ImmutableMapWrapper(new EntryLockingStringMap());
305 $result = new UploadResult(
306 $metadata->getFilename(),
307 $metadata->getUploadSize(),
308 $metadata->getMimeType(),
309 $extraMetadata,
310 $processingStatus,
311 ''
312 );
313
314 $this->rejectedUploadResult[] = $result;
315 }
316
317
321 public function getResults()
322 {
323 if ($this->processed) {
324 return array_merge($this->uploadResult, $this->rejectedUploadResult);
325 }
326
327 throw new IllegalStateException('Can not fetch results without processing the uploads.');
328 }
329
330
334 public function hasUploads()
335 {
336 if ($this->moved) {
337 return false;
338 }
339
340 $uploadedFiles = $this->flattenUploadedFiles($this->globalHttpState->request()->getUploadedFiles());
341
342 return (count($uploadedFiles) > 0);
343 }
344
345
351 protected function flattenUploadedFiles($uploadedFiles)
352 {
353 $recursiveIterator = new RecursiveIteratorIterator(
354 new RecursiveArrayIterator(
355 $uploadedFiles,
356 RecursiveArrayIterator::CHILD_ARRAYS_ONLY
357 ),
358 RecursiveIteratorIterator::LEAVES_ONLY
359 );
360
361 return iterator_to_array($recursiveIterator, false);
362 }
363
364
368 public function hasBeenProcessed()
369 {
370 return $this->processed;
371 }
372}
$result
$location
Definition: buildRTE.php:44
An exception for terminatinating execution or to throw for unit testing.
getFilename()
The filename supplied by the browser.
Definition: Metadata.php:73
getMimeType()
Client supplied mime type of the uploaded.
Definition: Metadata.php:118
getUploadSize()
This is always the original file size which was determined by the http service.
Definition: Metadata.php:105
const REJECTED
Upload got rejected by a processor.
rejectFailedUpload(UploadedFileInterface $file, Metadata $metadata)
Reject a failed upload with the given metadata.
moveFilesTo($destination, $location=Location::STORAGE)
@inheritDoc
regenerateUploadResultWithPath(UploadResult $result, $path)
Generate an exact copy of the result with the given path.
regenerateUploadResultWithCopyError(UploadResult $result, $errorReason)
Creates a clone of the given result and set the status to rejected with the passed error message.
__construct(PreProcessorManager $processorManager, Filesystems $filesystems, GlobalHttpState $globalHttpState)
FileUploadImpl constructor.
selectFilesystem($location)
Selects the correct filesystem by the given Location constant.
moveOneFileTo(UploadResult $uploadResult, $destination, $location=Location::STORAGE, $file_name='', $override_existing=false)
Moves a single File (the attributes, metadata and upload-status of which are contained in UploadResul...
static ofPsr7Stream(StreamInterface $stream)
Create a FileStream from a Psr7 compliant stream.
Definition: Streams.php:69
process()
Invokes all preprocessors for each uploaded file in the sequence they got registered.
const TEMPORARY
The ILIAS temporary directory.
Definition: Location.php:38
const CUSTOMIZING
The filesystem within the web root where all the skins and plugins are saved.
Definition: Location.php:33
const WEB
The filesystem within the ilias web root.
Definition: Location.php:23
const STORAGE
The filesystem outside of the ilias web root.
Definition: Location.php:28
Interface GlobalHttpState.
static filesystems()
Returns the loaded filesystems.