Хранение и раздача файлов через Ensi Storage
Файлы в платформе делятся на 2 вида: публичные и приватные. Все они хранятся на физическом диске Ensi Storage, который доступен в каждом сервисе в директории storage/ensi.
Структура этого диска:
.
├── public
│ ├── domain_1
│ │ └── hash
│ │ └── filename.png
│ └── domain_2
└── protected
├── domain_1
└── domain_2
При использовании [Ensi Local Ctl] можно увидеть содержимое диска в ensi-local-ctl/data/es/data
Для скачивания файлов существует 3 способа:
- Домен https://es-public.project.ru - раздаёт файлы директории public. Доступен всем в интернете. При необходимости эту ссылку можно передать в imgproxy и получить обработанную картинку
- Домен https://es-protected.project.ru - раздаёт файлы директории protected. Доступен только внутри защищенной среды, например под vpn
- Эдпоинт раздачи приватных файлов на витрины/админки (Подробнее ниже)
При использовании [Ensi Local Ctl] домены подниманиюся с помощью ensi global start es
Для работы с файлами в Laravel разработан пакет laravel-ensi-filesystem. После установки пакета появится 3 новых сконфигурированных диска:
- Диски для работы с файлами текущего домена - с их помощью можно загружать файлы в Ensi Storage и генерировать ссылки для раздачи с доменов
- ensi_{domain}_public - соответствует папке public/domain/
- ensi_{domain}_protected - соответствует папке protected/domain/
- Диск для чтения файлов чужих доменов
- ensi - соответсвует корню диска
Как загружать файлы
Для загрузки файла создаётся отдельный эндпоинт, например /module/entity/{id}:upload-file
Принимает запрос в формате multipart/form-data
и в Action реализует примерно следующий код:
use Ensi\LaravelEnsiFilesystem\EnsiFilesystemManager;
class SaveFileAction
{
public function __construct(protected EnsiFilesystemManager $fileManager)
{
}
public function execute(int $modelId, UploadedFile $file): Model
{
/** @var Model $model */
$model = Model::findOrFail($customerId);
$hash = Str::random(20);
$extension = $file->getClientOriginalExtension();
$fileName = "{$modelId}_{$hash}.{$extension}";
$hashedSubDirs = $this->fileManager->getHashedDirsForFileName($fileName);
$disk = Storage::disk($this->fileManager->publicDiskName());
$path = $disk->putFileAs("model/{$hashedSubDirs}", $file, $fileName);
if (!$path) {
throw new RuntimeException("Unable to save file $fileName to directory model/{$hashedSubDirs}");
}
if ($model->file) {
$disk->delete($model->file);
}
$model->file = $path;
$model->save();
return $model;
}
}
Удаление файлов происходит по аналогии
Какие данные о файлах хранить в БД
В БД хранится только путь до файла
Какие данные о файле сервис отдаёт
Для отдачи данных используется класс \Ensi\LaravelEnsiFilesystem\Models\EnsiFile
, который на основании пути генерирует все необходимые для ответа данные. Например: EnsiFile::public($model->file)
. Также в BaseJsonResource
существуют хелперы, например $this->mapPublicFileToResponse($this->file)
Как раздать приватный файл пользователю
Для раздачи файла требуется, чтобы пользователь прошел аутентификацию и можно было проверить доступ к запрашиваемому файлу.
Для этого в gui-backend реализуется эндпоинт, который получает данные о запрашиваевом файле, проверяет доступ пользователя, и отдаёт файл, если проверка пройдена
Данные о файле также генерирует предварительно gui-backend. Состоят они из следующих полей:
- entity - сущность, файл которой мы пытаемся скачать, например pim/category
- entity_id - конкретная сущность, файл которой мы пытаемся скачать, например ID категории
- file_type - если у сущности несколько видов файлов, то тут указывается типа (по сути это обозначение поля в БД), например preview
- file - если файлов одного типа много, то можно указать path|id на конкретный файл
Пример реализации можно посмотреть тут