Перейти к основному содержимому

Хранение и раздача файлов через 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 на конкретный файл

Пример реализации можно посмотреть тут