105 lines
4.0 KiB
PHP
105 lines
4.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\FileManager\Model;
|
|
|
|
use Esun\FileManager\Model\ResourceModel\File as FileResource;
|
|
use Esun\FileManager\Model\Security\FilePolicy;
|
|
use Magento\Framework\App\Filesystem\DirectoryList;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Framework\Filesystem;
|
|
use Magento\Framework\Filesystem\Directory\WriteInterface;
|
|
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
|
|
use Magento\Framework\UrlInterface;
|
|
use Magento\Store\Model\StoreManagerInterface;
|
|
|
|
class UploadService
|
|
{
|
|
private WriteInterface $mediaDirectory;
|
|
|
|
public function __construct(
|
|
Filesystem $filesystem,
|
|
private readonly Config $config,
|
|
private readonly FilePolicy $filePolicy,
|
|
private readonly Filename $filename,
|
|
private readonly FileUploaderFactory $uploaderFactory,
|
|
private readonly FileFactory $fileFactory,
|
|
private readonly FileResource $fileResource,
|
|
private readonly StoreManagerInterface $storeManager,
|
|
private readonly TimezoneInterface $timezone
|
|
) {
|
|
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
|
|
}
|
|
|
|
/**
|
|
* @return array{id: int, filename: string, url: string}
|
|
* @throws LocalizedException
|
|
*/
|
|
public function upload(array $uploadedFile): array
|
|
{
|
|
if (!$this->config->isEnabled()) {
|
|
throw new LocalizedException(__('File Manager is disabled.'));
|
|
}
|
|
|
|
$validated = $this->filePolicy->validate($uploadedFile);
|
|
$safeName = $this->filename->sanitize(
|
|
(string) ($uploadedFile['name'] ?? ''),
|
|
$validated['extension']
|
|
);
|
|
$relativeDirectory = $this->config->getUploadPath() . '/'
|
|
. $this->timezone->date()->format('Y/m');
|
|
$this->assertSafeRelativePath($relativeDirectory);
|
|
$this->mediaDirectory->create($relativeDirectory);
|
|
|
|
$uploader = $this->uploaderFactory->create(['fileId' => $uploadedFile]);
|
|
$uploader->setAllowedExtensions($this->config->getAllowedExtensions());
|
|
$uploader->setAllowRenameFiles(true);
|
|
$uploader->setFilesDispersion(false);
|
|
$result = $uploader->save($this->mediaDirectory->getAbsolutePath($relativeDirectory), $safeName);
|
|
if (!$result || empty($result['file'])) {
|
|
throw new LocalizedException(__('The file could not be saved.'));
|
|
}
|
|
|
|
$storedName = ltrim((string) $result['file'], '/');
|
|
$relativePath = $relativeDirectory . '/' . $storedName;
|
|
$this->assertSafeRelativePath($relativePath);
|
|
$url = rtrim(
|
|
$this->storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_MEDIA),
|
|
'/'
|
|
) . '/' . str_replace('%2F', '/', rawurlencode($relativePath));
|
|
|
|
try {
|
|
$model = $this->fileFactory->create();
|
|
$model->setData([
|
|
'filename' => $storedName,
|
|
'filepath' => $relativePath,
|
|
'url' => $url,
|
|
'extension' => $validated['extension'],
|
|
'mime_type' => $validated['mime_type'],
|
|
'size' => (int) ($uploadedFile['size'] ?? 0),
|
|
]);
|
|
$this->fileResource->save($model);
|
|
} catch (\Throwable $exception) {
|
|
if ($this->mediaDirectory->isFile($relativePath)) {
|
|
$this->mediaDirectory->delete($relativePath);
|
|
}
|
|
throw new LocalizedException(__('The file record could not be saved.'), $exception);
|
|
}
|
|
|
|
return ['id' => (int) $model->getId(), 'filename' => $storedName, 'url' => $url];
|
|
}
|
|
|
|
/**
|
|
* @throws LocalizedException
|
|
*/
|
|
private function assertSafeRelativePath(string $path): void
|
|
{
|
|
$normalized = str_replace('\\', '/', $path);
|
|
if ($normalized !== trim($normalized, '/') || str_contains($normalized, '..')
|
|
|| !preg_match('#^uploads(?:/[\p{L}\p{N}._-]+)+$#u', $normalized)
|
|
) {
|
|
throw new LocalizedException(__('The generated upload path is invalid.'));
|
|
}
|
|
}
|
|
}
|