# FilesDirectoryService ## Назначение Сервис отвечает за хранение и раздачу файлов/медиа, валидацию категорий и параметров, а также за сценарии «отложенной загрузки» через резервирование токенов (для загрузки файлов от 1С). Предоставляет низкоуровневые операции (пути/метаданные/удаление) и высокоуровневые (загрузка, резервирование, превью по конфигу). ## Публичные методы ### Пути/файлы/метаданные * `GetFilePath(fileId): string` - получить физический путь хранения. * `DeleteFile(fileId): void` - удалить файл. * `ValidExistFile(fileId): bool` - проверить существование файла. * `GetFileInfo(fileId): FileDescriptor` - базовая информация по файлу. * `GetFileInfoByHashAndCategory(hash, category): FileDescriptor` - получить файл по хэшу и категории (для дедупликации). * `GetFileConfig(category): FileImageConfig` - конфигурация обработки/хранения для категории. ### Загрузка * `UploadFile(category, stream): FileDescriptor` - загрузить файл в указанную категорию, вернуть дескриптор. * `ValidExistFileAndCategoryAsync(filesIds, category): bool` - проверить, что все файлы существуют и принадлежат категории. ### Резервирование и отложенная загрузка * `ReserveUploadFilesAsync(reserves[]): FileUploadReserve[]` - зарезервировать параметры будущих файлов и выдать токены/идентификаторы резерва. * `QueryReservedFileAsync(reserveIds[]): FileDescriptor?[]` - узнать, загружен ли файл по резервации. * `UploadReservedFileAsync(reserveTokenId, stream): FileDescriptor` - загрузить файл по ранее выданному токену. ## Конфигурация сервиса ### Модель конфигурации * `FilesServiceConfig` - корневые параметры: каталог хранилища и массив конфигов по категориям. * `FileImageConfig` - правила обработки по категории: JPEG-качество, лимиты размеров/веса, ресайзы, набор превью, MIME, доступы. Также может содержать вложенные `Previews` - каскад настроек для генерации производных изображений. Ключевые поля `FileImageConfig`: * `Quality` - качество изображения при сохранении; * `ResizeToWidth` / `ResizeToHeight` / `MaxSideSize` / `ResizeToWH` - правила изменения размеров (с приоритетами); * `MaxFileSize` - ограничение размера исходного файла; * `Directory` - подкаталог хранения для категории; * `Mime` - ожидаемый MIME тип; * `Previews[]` - список правил для генерации превью; * `Accesses[]` - список доступов, которым разрешена работа с категорией (если используется контроль доступа на уровне медиа). ### Пример конфигурации (фрагменты) В `config.json` (приложен к коду, как пример) заданы категории `Product`, `UserIcon`, `Brand`, `Article`, `ArticleTile`, `Review` - каждая со своими лимитами, директориями и превью: * `Product`: `MaxSideSize: 2048`, превью 256/512, `Mime: image/jpeg`. * `UserIcon`: обрезка/ресайз до `ResizeToWH: [512, 512]`. * `Article` и `ArticleTile`: с `Accesses: [100]` (пример ограничения по доступам). * `Review`: `MaxSideSize: 1920` и превью `128x128`. Корневой каталог хранилища - `FilesStorageDirectory: "filesstorage"`. ## Модели ### `FileDescriptor` ``` string Id - идентификатор файла, он же название файла string Hash - SHA256 string Category - категория файла FileTypeEnum Type - тип файла (пока используется только Image = 1) string Mime DateTime Created string Attributes - дополнительная информация о файле ``` ### `FileUploadReserve` Модель резервации: `ReserveId`, `TokenId` (для последующей загрузки), `Category`, `FileId` (заполняется, если файл уже загружен). Используется для отложенной загрузки файла от мастер-системы. ## Типовые потоки ``` [DirectoryService] --v [Connector1CService] -> [FilesDirectoryService] -> [Static Files] <- [nginx] <-> PWA ^ [1C] --^ ``` ### Получения файла Для получения файла нужно знать его категорию и его Id. Балансировочный сервер настраивается на доступ к физическим файлам по ссылке (корневой каталог, который указан в конфиге)/(Название категории)/(ID файла). Таким образом можно обращаться напрямую к статике минуя сервисы. ### Прямая загрузка 1. Клиент (обычно админская PWA) выбирает категорию → 2) вызывает `UploadFile(category, stream)` → 3) сервис валидирует MIME/размер/правила категории, применяет ресайз/качество, создаёт превью → 4) возвращает `FileDescriptor` (ID, MIME, Category и т. д.). ### Отложенная загрузка (резервирование) 1. Сервис/Пользователь запрашивает токен через `ReserveUploadFilesAsync` 2. Позже, когда есть возможность, выполняется `UploadReservedFileAsync(token, stream)`; 3. Статус можно опрашивать через `QueryReservedFileAsync(reserveIds)`; 4. После успешной загрузки `FileId` становится доступным в модели резерва и можно его обновить у инициатора. Так Directory синхронизируя данные с мастер системой, может зарезервировать файлы на загрузку. В качестве TokenId указывается Id файла из мастер системы. Данные о товарах загружаются большим скопом и быстро, а файлы загружаются долго. Поэтому приходится резервировать место для файлов, чтобы мастер система могла потом сама загрузить нужные файлы в FilesDirectory, когда будет возможность. Directory по заданию опрашивает FilesDirectory на предмет загрузки зарезервированных файлов, и обновляет информацию у себя. --- ## Валидация, инварианты и безопасность * **Соответствие категории:** операции `ValidExistFileAndCategoryAsync` и `GetFileInfoByHashAndCategory` защищают от подмены категории/хэша и ошибок склейки. * **Лимиты и преобразования:** применяются по `FileImageConfig` с приоритетами правил ресайза (ширина → высота → max side → точный WH). * **Доступы к категориям:** при наличии `Accesses[]` доступ ограничивается доступами/ролями (пример - статьи/тайлы). Это позволяет хранить служебные медиа, недоступные обычным пользователям. * **Дедупликация:** поиск по `Hash + Category` позволяет повторно использовать уже загруженный файл. --- Если нужно, оформлю это как отдельный раздел в документе и добавлю таблицу категорий из `config.json` (категория → директория → лимиты → превью → доступы). ## DB Содержит таблицы и индексы ``` -- Migration 1 files ( Id TEXT PRIMARY KEY NOT NULL, Category TEXT NOT NULL DEFAULT '', Path TEXT NOT NULL DEFAULT '', -- физический путь к файлу Hash TEXT NOT NULL DEFAULT '', Type INTEGER, Mime TEXT NOT NULL DEFAULT '', Created NUMBER, CreatorId NUMBER, Attributes TEXT); reserves ( ReserveId INTEGER PRIMARY KEY AUTOINCREMENT, TokenId TEXT NOT NULL DEFAULT '', Category TEXT NOT NULL DEFAULT '', FileId TEXT NOT NULL DEFAULT ''); INDEX files_hash on files (hash); ``` [Назад](../../../index.md)