Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
Uploader.php
1<?php
2
4
10
12{
14
16 {
17 $this->controller = $controller;
18 }
19
21 {
22 return $this->controller;
23 }
24
25 public function upload(Chunk $chunk, string $token = null): UploadResult
26 {
27 $controller = $this->getController();
28 $uploadResult = new UploadResult();
29 if ($chunk->isFirst())
30 {
31 // Common file validation (uses in CFile::SaveFile)
32 $commitOptions = $controller->getCommitOptions();
33 $error = \CFile::checkFile(
34 [
35 'name' => $chunk->getName(),
36 'size' => $chunk->getFileSize(),
37 'type' => $chunk->getType()
38 ],
39 0,
40 false,
41 false,
42 $commitOptions->isForceRandom(),
43 $commitOptions->isSkipExtension()
44 );
45
46 if ($error !== '')
47 {
48 return $this->handleUploadError(
49 $uploadResult->addError(new UploaderError('CHECK_FILE_FAILED', $error)),
51 );
52 }
53
54 // Controller Validation
55 $validationResult = $chunk->validate($controller->getConfiguration());
56 if (!$validationResult->isSuccess())
57 {
58 return $this->handleUploadError($uploadResult->addErrors($validationResult->getErrors()), $controller);
59 }
60
61 ['width' => $width, 'height' => $height] = $validationResult->getData();
62 $chunk->setWidth((int)$width);
63 $chunk->setHeight((int)$height);
64
65 $uploadRequest = new UploadRequest($chunk->getName(), $chunk->getType(), $chunk->getSize());
66 $uploadRequest->setWidth($chunk->getWidth());
67 $uploadRequest->setHeight($chunk->getHeight());
68
69 // Temporary call for compatibility
70 // $canUploadResult = $controller->canUpload($uploadRequest);
71 $canUploadResult = call_user_func([$controller, 'canUpload'], $uploadRequest);
72 if (($canUploadResult instanceof CanUploadResult) && !$canUploadResult->isSuccess())
73 {
74 return $this->handleUploadError($uploadResult->addErrors($canUploadResult->getErrors()), $controller);
75 }
76 else if (!is_bool($canUploadResult) || $canUploadResult === false)
77 {
78 return $this->handleUploadError(
79 $uploadResult->addError(new UploaderError(UploaderError::FILE_UPLOAD_ACCESS_DENIED)),
81 );
82 }
83
84 $createResult = TempFile::create($chunk, $controller);
85 if (!$createResult->isSuccess())
86 {
87 return $this->handleUploadError($uploadResult->addErrors($createResult->getErrors()), $controller);
88 }
89
91 $tempFile = $createResult->getData()['tempFile'];
92 $uploadResult->setTempFile($tempFile);
93 $uploadResult->setToken($this->generateToken($tempFile));
94
95 $controller->onUploadStart($uploadResult);
96 if (!$uploadResult->isSuccess())
97 {
98 return $this->handleUploadError($uploadResult, $controller);
99 }
100 }
101 else
102 {
103 if (empty($token))
104 {
105 return $this->handleUploadError(
106 $uploadResult->addError(new UploaderError(UploaderError::EMPTY_TOKEN)),
108 );
109 }
110
111 $guid = $this->getGuidFromToken($token);
112 if (!$guid)
113 {
114 return $this->handleUploadError(
115 $uploadResult->addError(new UploaderError(UploaderError::INVALID_SIGNATURE)),
117 );
118 }
119
120 $tempFile = TempFileTable::getList([
121 'filter' => [
122 '=GUID' => $guid,
123 '=UPLOADED' => false,
124 ],
125 ])->fetchObject();
126
127 if (!$tempFile)
128 {
129 return $this->handleUploadError(
130 $uploadResult->addError(new UploaderError(UploaderError::UNKNOWN_TOKEN)),
132 );
133 }
134
135 $uploadResult->setTempFile($tempFile);
136 $uploadResult->setToken($token);
137
138 $appendResult = $tempFile->append($chunk);
139 if (!$appendResult->isSuccess())
140 {
141 return $this->handleUploadError($uploadResult->addErrors($appendResult->getErrors()), $controller);
142 }
143 }
144
145 if ($uploadResult->isSuccess() && $chunk->isLast())
146 {
147 $commitResult = $tempFile->commit($controller->getCommitOptions());
148 if (!$commitResult->isSuccess())
149 {
150 return $this->handleUploadError($uploadResult->addErrors($commitResult->getErrors()), $controller);
151 }
152
153 $fileInfo = $this->createFileInfo($uploadResult->getToken());
154 $uploadResult->setFileInfo($fileInfo);
155 $uploadResult->setDone(true);
156
157 $controller->onUploadComplete($uploadResult);
158 if (!$uploadResult->isSuccess())
159 {
160 return $this->handleUploadError($uploadResult, $controller);
161 }
162 }
163
164 return $uploadResult;
165 }
166
167 private function handleUploadError(UploadResult $uploadResult, UploaderController $controller): UploadResult
168 {
169 $controller->onUploadError($uploadResult);
170
171 if (!$uploadResult->isSuccess())
172 {
173 $tempFile = $uploadResult->getTempFile();
174 if ($tempFile !== null && $tempFile->state !== State::DELETED)
175 {
176 $tempFile->delete();
177 }
178 }
179
180 return $uploadResult;
181 }
182
183 public function generateToken(TempFile $tempFile): string
184 {
185 $guid = $tempFile->getGuid();
186 $salt = $this->getTokenSalt([$guid]);
187 $signer = new Signer();
188
189 return $signer->sign($guid, $salt);
190 }
191
192 private function getGuidFromToken(string $token): ?string
193 {
194 $parts = explode('.', $token, 2);
195 if (count($parts) !== 2)
196 {
197 return null;
198 }
199
200 [$guid, $signature] = $parts;
201 if (empty($guid) || empty($signature))
202 {
203 return null;
204 }
205
206 $salt = $this->getTokenSalt([$guid]);
207 $signer = new Signer();
208
209 if (!$signer->validate($guid, $signature, $salt))
210 {
211 return null;
212 }
213
214 return $guid;
215 }
216
217 private function getTokenSalt($params = []): string
218 {
219 $controller = $this->getController();
220 $options = $controller->getOptions();
221 ksort($options);
222
223 $fingerprint =
224 $controller instanceof CustomFingerprint
225 ? $controller->getFingerprint()
226 : (string)\bitrix_sessid()
227 ;
228
229 return md5(serialize(
230 array_merge(
231 $params,
232 [
234 $options,
235 $fingerprint,
236 ]
237 )
238 ));
239 }
240
241 public function load(array $ids): LoadResultCollection
242 {
243 $controller = $this->getController();
244 if ($controller instanceof CustomLoad)
245 {
246 return $controller->load($ids);
247 }
248
249 $results = new LoadResultCollection();
250 [$bfileIds, $tempFileIds] = $this->splitIds($ids);
251 $fileOwnerships = new FileOwnershipCollection($bfileIds);
252
253 // Files from b_file
254 if ($fileOwnerships->count() > 0)
255 {
256 $controller = $this->getController();
257 if ($controller->canView())
258 {
259 $controller->verifyFileOwner($fileOwnerships);
260 }
261
262 foreach ($fileOwnerships as $fileOwnership)
263 {
264 if ($fileOwnership->isOwn())
265 {
266 $loadResult = $this->loadFile($fileOwnership->getId());
267 }
268 else
269 {
270 $loadResult = new LoadResult($fileOwnership->getId());
271 $loadResult->addError(new UploaderError(UploaderError::FILE_LOAD_ACCESS_DENIED));
272 }
273
274 $results->add($loadResult);
275 }
276 }
277
278 // Temp Files
279 if (count($tempFileIds) > 0)
280 {
281 foreach ($tempFileIds as $tempFileId)
282 {
283 $loadResult = $this->loadTempFile($tempFileId);
284 $results->add($loadResult);
285 }
286 }
287
288 return $results;
289 }
290
291 public function remove(array $ids): RemoveResultCollection
292 {
293 $controller = $this->getController();
294 if ($controller instanceof CustomRemove)
295 {
296 return $controller->remove($ids);
297 }
298
299 $results = new RemoveResultCollection();
300 [$bfileIds, $tempFileIds] = $this->splitIds($ids);
301
302 // Files from b_file
303 if (count($bfileIds) > 0)
304 {
305 $fileOwnerships = new FileOwnershipCollection($bfileIds);
306 if ($controller->canRemove())
307 {
308 $controller->verifyFileOwner($fileOwnerships);
309 }
310
311 foreach ($fileOwnerships as $fileOwnership)
312 {
313 $removeResult = new RemoveResult($fileOwnership->getId());
314 if ($fileOwnership->isOwn())
315 {
316 // TODO: remove file
317 }
318 else
319 {
320 $removeResult->addError(new UploaderError(UploaderError::FILE_REMOVE_ACCESS_DENIED));
321 }
322
323 $results->add($removeResult);
324 }
325 }
326
327 // Temp Files
328 if (count($tempFileIds) > 0)
329 {
330 foreach ($tempFileIds as $tempFileId)
331 {
332 $removeResult = new RemoveResult($tempFileId);
333 $results->add($removeResult);
334
335 $guid = $this->getGuidFromToken($tempFileId);
336 if (!$guid)
337 {
338 $removeResult->addError(new UploaderError(UploaderError::INVALID_SIGNATURE));
339 continue;
340 }
341
342 $tempFile = TempFileTable::getList([
343 'filter' => [
344 '=GUID' => $guid,
345 ],
346 ])->fetchObject();
347
348 if ($tempFile)
349 {
350 $tempFile->delete();
351 }
352 }
353 }
354
355 return $results;
356 }
357
358 public function getPendingFiles(array $tempFileIds): PendingFileCollection
359 {
360 $pendingFiles = new PendingFileCollection();
361 foreach ($tempFileIds as $tempFileId)
362 {
363 if (!is_string($tempFileId) || empty($tempFileId))
364 {
365 continue;
366 }
367
368 $pendingFile = new PendingFile($tempFileId);
369 $pendingFiles->add($pendingFile);
370
371 $guid = $this->getGuidFromToken($tempFileId);
372 if (!$guid)
373 {
374 $pendingFile->addError(new UploaderError(UploaderError::INVALID_SIGNATURE));
375
376 continue;
377 }
378
379 $tempFile = TempFileTable::getList([
380 'filter' => [
381 '=GUID' => $guid,
382 '=UPLOADED' => true,
383 ],
384 ])->fetchObject();
385
386 if (!$tempFile)
387 {
388 $pendingFile->addError(new UploaderError(UploaderError::UNKNOWN_TOKEN));
389
390 continue;
391 }
392
393 $pendingFile->setTempFile($tempFile);
394 }
395
396 return $pendingFiles;
397 }
398
399 private function loadFile(int $fileId): LoadResult
400 {
401 $result = new LoadResult($fileId);
402 if ($fileId < 1)
403 {
404 return $result->addError(new UploaderError(UploaderError::FILE_LOAD_FAILED));
405 }
406
407 $fileInfo = $this->createFileInfo($fileId);
408 if ($fileInfo)
409 {
410 $result->setFile($fileInfo);
411 }
412 else
413 {
414 return $result->addError(new UploaderError(UploaderError::FILE_LOAD_FAILED));
415 }
416
417 return $result;
418 }
419
420 private function loadTempFile(string $tempFileId): LoadResult
421 {
422 $result = new LoadResult($tempFileId);
423 $guid = $this->getGuidFromToken($tempFileId);
424 if (!$guid)
425 {
426 return $result->addError(new UploaderError(UploaderError::INVALID_SIGNATURE));
427 }
428
429 $tempFile = TempFileTable::getList([
430 'filter' => [
431 '=GUID' => $guid,
432 '=UPLOADED' => true,
433 ],
434 ])->fetchObject();
435
436 if (!$tempFile)
437 {
438 return $result->addError(new UploaderError(UploaderError::UNKNOWN_TOKEN));
439 }
440
441 $fileInfo = $this->createFileInfo($tempFileId);
442 if ($fileInfo)
443 {
444 $result->setFile($fileInfo);
445 }
446 else
447 {
448 return $result->addError(new UploaderError(UploaderError::FILE_LOAD_FAILED));
449 }
450
451 return $result;
452 }
453
454 private function createFileInfo($fileId): ?FileInfo
455 {
456 $fileInfo = is_int($fileId) ? FileInfo::createFromBFile($fileId) : FileInfo::createFromTempFile($fileId);
457 if ($fileInfo)
458 {
459 $downloadUrl = (string)UrlManager::getDownloadUrl($this->getController(), $fileInfo);
460 $fileInfo->setDownloadUrl($downloadUrl);
461 if ($fileInfo->isImage())
462 {
463 $config = $this->getController()->getConfiguration();
464 if ($config->shouldTreatOversizeImageAsFile())
465 {
466 $treatImageAsFile = $config->shouldTreatImageAsFile($fileInfo);
467 $fileInfo->setTreatImageAsFile($treatImageAsFile);
468 }
469
470 if (!$fileInfo->shouldTreatImageAsFile())
471 {
472 $rectangle = PreviewImage::getSize($fileInfo);
473 $previewUrl = (string)UrlManager::getPreviewUrl($this->getController(), $fileInfo);
474 $fileInfo->setPreviewUrl($previewUrl, $rectangle->getWidth(), $rectangle->getHeight());
475 }
476 }
477 }
478
479 return $fileInfo;
480 }
481
482 private function splitIds(array $ids): array
483 {
484 $fileIds = [];
485 $tempFileIds = [];
486 foreach ($ids as $id)
487 {
488 if (is_numeric($id))
489 {
490 $fileIds[] = (int)$id;
491 }
492 else
493 {
494 $tempFileIds[] = (string)$id;
495 }
496 }
497
498 return [$fileIds, $tempFileIds];
499 }
500}
validate(Configuration $config)
Definition Chunk.php:281
static createFromBFile(int $id)
Definition FileInfo.php:30
static getSize(FileInfo $fileInfo, PreviewImageOptions $options=null)
static create(Chunk $chunk, UploaderController $controller)
Definition TempFile.php:14
verifyFileOwner(FileOwnershipCollection $files)
generateToken(TempFile $tempFile)
Definition Uploader.php:183
__construct(UploaderController $controller)
Definition Uploader.php:15
UploaderController $controller
Definition Uploader.php:13
getPendingFiles(array $tempFileIds)
Definition Uploader.php:358
static getDownloadUrl(UploaderController $controller, FileInfo $fileInfo)
static getPreviewUrl(UploaderController $controller, FileInfo $fileInfo)