Автор должен признаться: статья родилась и выросла из довольно небольшой и не претендующей на откровение оптимизации сайта, описанного в другом материале. Сайт этот связан с музыкальной тематикой и, соответственно, активно отображает обложки альбомов, хранящиеся (до поры до времени) на сервере не в ФС, как обычно бывает, а в БД SQL Server (в BLOB-поле, о чём несложно догадаться); в старом варианте пора извлечения изображений из базы и сохранения их в виде файлов возникает в момент запроса альбомов пользователем, после чего (по истечении сессии) они удаляются.

Страница сайта с обложками

Чтобы почти окончательно развеять куцую интригу и помочь возможному читателю определиться с тем, имеет ли смысл продолжить чтение, опишу конечный результат оптимизации: обложки по-прежнему останутся в базе данных, причём в поле того же типа, но вот веб-сервер, отдающий в итоге изображения клиенту, будет «обманут» и станет обращаться за файлом знать не зная, что его на самом деле нет в ФС, а вызов идёт сразу и непосредственно к СУБД. Такой «обман» конечно же не является самоцелью – всё в основном затевалось ради уменьшения нагрузки на дисковую подсистему.

Старая реализация


Прежде всего необходимо напомнить структуру HTML-тэга img, обязательным у которого является лишь атрибут src:

<img src="some_path/file_name.jpg">

У несложных сайтов изображение, находящееся по URL в этом атрибуте, веб-сервер обычно напрямую извлекает из ФС, т. к. относительный путь к нему очень часто совпадает со структурой каталогов на сервере, а имя файла тоже берётся непосредственно из ФС; однако такое распространённое поведение, если брать конкретно IIS (именно он применяется в данном проекте), может быть заменено на совершенно другое за счёт модулей (расширений), когда изображения станут, к примеру, генерироваться на лету (т. е. физически они нигде не хранятся). Так вот статья затрагивает лишь первый вариант.

На этом сайте обложки располагаются не в файловой системе из-за желания «всё своё носить с собой» – чтобы резервная копия БД содержала полный набор информации, была самодостаточной; соответственно, дабы веб-сервер мог отдать изображение клиенту, оно, как уже говорилось, должно быть извлечено из базы данных и сохранено в виде файла, что добавляет лишнюю операцию: сначала читаем из БД, после чего записываем копию на диск, бездарно и теряя дисковое пространство, и нагружая эту подсистему. Ко всему прочему, если несколько разных пользователей относительно одновременно запросят одну и ту же обложку, то из-за того, что в uniGUI каждый из них представлен отдельной сессией, файл дополнительно продублируется ещё и для каждой из них.

Схематически и упрощённо описанное непотребство можно представить следующим образом:

Схема с копированием обложек

Если проиллюстрировать прошлую реализацию кодом, то его логика будет иметь довольно классический вид:

  1. Запрос на выборку данных возвращает, среди прочих, и BLOB-поле с обложкой (в данном случае на примере компонента spAlbums типа TFDStoredProc из состава FireDAC).
  2. Изображение из поля, в зависимости от формата (png, jpg…), загружается в соответствующего наследника TGraphic.
  3. Далее используется предоставляемая uniGUI функция uniImageToURL, сохраняющая изображение в нужную папку и возвращающая готовый URL для атрибута src.

uses
  System.SysUtils, System.Classes, Data.DB,
  Vcl.Graphics, Vcl.Imaging.jpeg, Vcl.Imaging.pngimage, Vcl.Imaging.GIFImg,
  uniGUIUtils;

function TMainForm.AlbumsToHTML: string;

  function CoverURL: string;
  var
    CoverFormat: string;
    Image: TGraphic;
    ImageStream: TStream;
  begin
    CoverFormat := spAlbums['CoverFormat'];

    if CoverFormat = 'jpg' then
      Image := TJPEGImage.Create;
    if CoverFormat = 'png' then
      Image := TPngImage.Create;
    if CoverFormat = 'gif' then
      Image := TGIFImage.Create;
    if CoverFormat = 'bmp' then
      Image := TBitmap.Create;

    try
      ImageStream := spAlbums.CreateBlobStream
        (
        spAlbums.FieldByName('Cover'),
        bmRead
        );
      try
        Image.LoadFromStream(ImageStream);
      finally
        ImageStream.Free;
      end;

      Result := uniImageToURL(Image);
    finally
      Image.Free;
    end;
  end;

const
  AlbumHTMLTemplate =
    '<div class="album">' +
      '<div class="cover-container">' +
        '<img src="%s" alt="Обложка альбома">' +
        ...
      '</div>' +
    '</div>';
begin
  spAlbums.First;

  while not spAlbums.Eof do
  begin
    Result := Result + Format( AlbumHTMLTemplate, [CoverURL, ...] );
    spAlbums.Next;
  end;
end;

Идеальный вариант


Если предаться мечтаниям и представить, что обложки по-прежнему находятся в БД в BLOB-поле, а IIS каким-то образом, почти без усилий и программирования с нашей стороны, сам извлекает их, то окажется, что SQL Server способен такую мечту исполнить – за счёт функционала FileTable, представляющего собой, если очень кратко, создаваемую разработчиком специального вида таблицу, каждая запись которой отображается в файл в NTFS; обращение к таким файлам ничем не отличается от стандартного (однако некоторые возможности не поддерживаются), т. е. их можно просматривать, изменять и удалять хоть через Проводник.

Если снова графически показать такую схему, то мы избавимся от копирования обложки в папку сайта:

Схема без копирования обложек

Данный вариант полностью совместим и с отложенной загрузкой изображений, настраиваемой скажем через атрибут loading, т. е. если пользователь не прокрутит страницу сайта до некоторых обложек, то они и не будут прочитаны из базы (написанное, разумеется, справедливо и для случая, когда в браузере вообще отключен показ изображений):

<img src="some_path/file_name.jpg" loading="lazy">

Чтобы воспользоваться этой манной небесной, ниспосланной SQL Server, потребуется, само-собой, выполнить некоторые действия как на стороне СУБД, так и на стороне IIS, чему собственно и посвящена оставшаяся практическая часть статьи. Но предварительно хотелось бы сделать небольшую ремарку и отметить, что конкуренты Microsoft тоже могут предложить тонкую работу с BLOB, но функционал уровня FileTable автору удалось найти лишь у Oracle – речь о Database File System (DBFS). Остальные же игроки предоставляют лишь условно традиционный (но неизбежно СУБД-специфичный) API, несколько примеров:

  1. PostgreSQL
  2. Interbase
  3. MySQL

Кстати, использующим Express-редакцию SQL Server наверняка весьма понравится то, что данные в файловых таблицах не учитываются при проверке лимита на размер БД в 10 Гб, т. к. реализация FileTable основывается на технологии FILESTREAM, как раз и свободной от этого ограничения у бесплатной версии.

Настройка SQL Server


По умолчанию FileTable-возможности отключены в SQL Server. Официальная документация содержит весьма подробную и внятную инструкцию по параметрам, которые необходимо задействовать как на уровне всего экземпляра, так и в конкретной базе данных, отчего не представляется разумным пересказывать здесь подобные тонкости – вместо этого автор сосредоточится на небольшом SQL-скрипте, выполняющем перенос обложек из существующей таблицы с альбомами в новую FileTable-таблицу (если же в Вашем случае файлы изначально в БД не хранятся, в отличие от данного проекта, то с их адаптацией к FileTable поможет документация).

Таблицы


Далее в скрипте предполагается следующая структура двух только что упомянутых таблиц (источника – с данными об альбомах, и приёмника – новой таблицы с обложками):

  • Источник dbo.Album:
    Поле Тип данных Описание
    ID integer Первичный ключ.
    Cover varbinary(max) BLOB-поле с собственно изображением, которое нужно перенести в новую таблицу.
    CoverFormat nvarchar(10) Тип изображения (jpg, png и т. д.).
    Прочие поля (название альбома, год издания и т. п.)...
  • Приёмник dbo.AlbumCover имеет жёстко заданную структуру со множеством полей (задача создания такой таблицы тривиальна и описана в справке), но нас заинтересуют лишь эти:
    Поле Тип данных Описание
    path_locator hierarchyid Первичный ключ.
    file_stream varbinary(max) Содержимое файла (в нашем случае это изображение).
    name nvarchar(255) Название файла (с расширением), отображаемое в ФС.
    Прочие поля...
Так как обложка является подчинённой по отношению к альбому сущностью, то, имей мы обычную таблицу, в приёмник следовало бы добавить поле со ссылкой на альбом, однако набор полей файловой таблицы не может быть расширен, как чуть выше говорилось, поэтому придётся модифицировать источник: вместо двух полей Cover и CoverFormat он в конечном итоге станет содержать лишь одно – идентификатор обложки из поля path_locator:
Поле Тип данных Описание
CoverID hierarchyid Внешний ключ на приёмник.
Прочие поля...

Скрипт


Все действия с базой логически делятся на несколько этапов:

  1. Банальнейшее по реализации добавление поля CoverID в источник:

    ALTER TABLE dbo.Album
    ADD CoverID hierarchyid;
  2. Копирование данных в приёмник и заполнение только что созданного поля CoverID (про неожиданное применение конструкции MERGE пояснение дано ниже):

    DECLARE @Covers TABLE
      (
      AlbumID integer NOT NULL,
      path_locator hierarchyid NOT NULL
      );
    
    MERGE dbo.AlbumCover AS cover
    USING
      (
      SELECT
        ID,
        CoverID,
        Cover,
        CAST( NEWID() as nvarchar(max) ) + '.' + CoverFormat AS FileName
      FROM
        dbo.Album
      ) AS album ON album.CoverID = cover.path_locator
    WHEN NOT MATCHED THEN
      INSERT (file_stream, name)
      VALUES (album.Cover, album.FileName)
    OUTPUT
      album.ID,
      INSERTED.path_locator
    INTO @Covers;
    
    UPDATE dbo.Album
    SET CoverID = path_locator
    FROM @Covers
    WHERE ID = AlbumID;

    Использование MERGE вместо конструкции INSERT продиктовано невозможностью в последней обратиться в блоке OUTPUT к полям любых задействованных таблиц (допустимы только поля той, в которую и происходит вставка). Другими словами, было бы нечем заполнить поле AlbumID табличной переменной @Covers, т. к. обращение ниже выполняется к таблице-источнику, а вставка идёт в dbo.AlbumCover:

    ...
    OUTPUT
      album.ID,
    ...
  3. Удаление ставших ненужными полей в источнике:

    ALTER TABLE dbo.Album
    DROP COLUMN Cover, CoverFormat;

    Важно отметить, что исчезновение полей не приведёт к уменьшению размера БД и возврату освободившегося места ОС – для этого потребуется явно выполнить команду SHRINKDATABASE.

Почти всё, теперь обложки доступны по специальному UNC-пути, возвращаемому функцией FileTableRootPath:

SELECT FileTableRootPath('dbo.AlbumCover');

Оговорка про «почти» связана с тем, что читатель может столкнуться с отсутствием доступа при попытке обратиться к означенным файлам – в этом случае следует ознакомиться с той частью следующего раздела, где описывается нюанс с правами. Также для определённости примем, что наш путь выглядит как \\ServerName\MSSQLSERVER\DatabaseName\AlbumCover (далее в другом примере будет отсылка к нему, где для наглядности потребуется конкретное значение).

Настройка веб-сервера


Хотя в текущем разделе демонстрация идёт на примере IIS, но большинство вещей применимы и к любому другому веб-серверу, ибо являются скорее ОС- и СУБД-специфичными.

Права на чтение


Если снова вернуться к атрибуту src тэга img, то становится очевидной небольшая проблема: путь в атрибуте должен быть относительным, а нужные изображения доступны лишь по UNC-пути. Решение довольно просто́ и заключается в создании символической ссылки в папке сайта; если условиться, что физически сайт располагается в каталоге c:\IIS\SiteName, а ссылка будет называться Covers, то её добавление выполняется следующей командой в консоли:

mklink /d "c:\IIS\SiteName\Covers" "\\ServerName\MSSQLSERVER\DatabaseName\AlbumCover"

Теперь атрибут src станет возможно заполнять подобным образом:

<img src="Covers/3AE39458-8925-448A-A5F1-0C0A4524ACF0.jpg">

Доработки Delphi-кода, дающие показанное значение для src, будут приведены в конце статьи, а пока же остался важный нюанс с правами (в нашем случае достаточно лишь на чтение), причём делящийся на две части:

  1. Предоставление доступа (на уровне ФС) пользователю IUSR и группе IIS_IUSRS для созданной символической ссылки:

    Настройка символической ссылки через графический интерфейс
  2. Одно из неприятных ограничений файловых таблиц заключается в отсутствии поддержки ACL, вследствие чего пользователь, от имени которого выполняется пул приложений IIS, должен получить права на файлы иным способом – средствами самой СУБД, за доступ к ним полностью и отвечающей. Делается всё совершенно стандартным для SQL Server образом, путём создания пары «Имя входа (логин)-пользователь БД»:
    • Логин создаётся на основе Windows-пользователя вида IIS APPPOOL\<Имя нужного пула>.

      CREATE LOGIN [IIS APPPOOL\YourPoolName] FROM WINDOWS;
    • После чего с ним связывается новый пользователь базы.

      CREATE USER FileTableReader FOR LOGIN [IIS APPPOOL\YourPoolName];
    • Которому выдаётся разрешение на чтение таблицы.

      GRANT SELECT ON dbo.AlbumCover TO FileTableReader;

Проверка подлинности


Последнее, что требуется настроить, относится непосредственно к IIS, а именно к проверке подлинности на сайте – чтобы она выполнялась от имени пула приложений, чуть выше только что получившего права на чтение обложек (если в сценарии читателя проверка обязана проходить от иного пользователя, то данный раздел можно пропустить, но при этом необходимо добавить ещё одну пару «логин-пользователь» как описано в предыдущем разделе, связав имя входа со значением из параметров проверки подлинности).

Настройка проверки подлинности через Диспетчер служб IIS

Новая реализация


В заключение осталось привести новый, значительно облегчённый по сравнению с предыдущим вариантом, код, ибо из БД уже не нужно извлекать само изображение – достаточно лишь получить имя файла с ним (из поля dbo.AlbumCover.name, имеющего в примере псевдоним CoverFileName) и в неизменном виде подставить в атрибут src:

uses
  System.SysUtils;

function TMainForm.AlbumsToHTML: string;
const
  AlbumHTMLTemplate =
    '<div class="album">' +
      '<div class="cover-container">' +
        '<img src="%s/%s" alt="Обложка альбома">' +
        ...
      '</div>' +
    '</div>';
  SymbolicLinkName = 'Covers';
begin
  spAlbums.First;

  while not spAlbums.Eof do
  begin
    Result := Result + Format( AlbumHTMLTemplate, [SymbolicLinkName, spAlbums['CoverFileName'], ...] );
    spAlbums.Next;
  end;
end;

Комментарии (37)


  1. akhkmed
    27.09.2021 22:17
    +8

    всё в основном затевалось ради уменьшения нагрузки на дисковую подсистему

    И вправду уменьшилось? За счёт чего? Как сильно?


    1. SergeyPyankov Автор
      28.09.2021 01:35

      Замеров не производилось, но возникает встречный вопрос: почему нагрузка должна как минимум остаться на том же уровне (или даже вырасти), если была исключена операция по созданию копии обложки?

      что добавляет лишнюю операцию: сначала читаем из БД, после чего записываем копию на диск


      1. akhkmed
        28.09.2021 22:05
        +1

        Общая нагрузка вырастет, поскольку ACID в базах данных достигается достаточно дорогой ценой большего использования диска/памяти/cpu.

        Не стану спорить, что в современных СУБД кеширование реализовано отлично и за счёт этого вы получаете более отзывчивую систему. Но вместе с этим вы получаете слишком большую побочную нагрузку, из-за чего с ростом нагрузки и количества blob вы замедлитесь раньше и сильнее, чем при использовании других подходов к хранению.


        1. SergeyPyankov Автор
          29.09.2021 13:23

          Общая нагрузка вырастет, поскольку ACID в базах данных достигается достаточно дорогой ценой

          Совершенно согласен, накладные расходы в случае БД окажутся несколько выше, о чём, собственно, уже писал ниже:

          описанное решение даже в самом распрекрасном случае будет лишь на уровне ФС, т. к. она, по определению, и создана для наилучшего обращения с файлами

          Не упрёка ради, а прояснения для, Ваши изначальные вопросы скорее всего появились из-за беглого прочтения статьи, потому что в этом проекте изображения сразу, изначально находились в базе, поэтому не получится сравнить сделанную оптимизацию с иным способом хранения.

          И на всякий случай повторюсь:

          Материал не про выбор места хранения, а про неизвестный многим способ извлечения файлов из БД, когда выбор уже сделан не в пользу ФС.


  1. rrrad
    27.09.2021 22:30
    +8

    поставить reverse-proxy с кешем перед веб-сервером и отдавать данные картинки прямо из БД (например, кодируя ид в имени файла) и нагрузка на дисковую подсистему именно на операциях чтения картинок упадёт до пренебрежимо малых значений. Такое решение вполне универсальное.


  1. harios
    27.09.2021 23:31
    +1

    И каково это, использовать унигуй? Как работает? Какие нюансы?


    1. SergeyPyankov Автор
      28.09.2021 12:28

      Мой опыт использования uniGUI не является показательным, т. к., во-первых, этот фреймворк применялся только в одном (данном) проекте, и, во-вторых, он не предназначен для сайтов с произвольным дизайном, каким является описанный в статье (хотя чисто технически это и возможно сделать, о чём рассказывает прошлая статья). Однако вот служебный сайт, предназначенный для управления содержимым первого (публичного), uniGUI позволил создать очень быстро и качественно.

      На Ваш вопрос наиболее полезно смогут ответить те, кто применяет uniGUI по прямому назначению — переносят настольное приложение в веб, например.


  1. Alexufo
    27.09.2021 23:40
    +19

    IIS, Delphy, картинки в бд? Ребята, куда то вы не туда свернули.


    1. harios
      27.09.2021 23:49
      +4

      А куда стоило по вашему мнению? Пойти по дорожке React/Vue и NodeJS? PWA? Или сдуть пыть с Java Servlet?)


      1. Alexufo
        28.09.2021 04:59

        Ну как-то вы непонятно спрашиваете. Фронт к дельфям можно любой привязать, судя по всему такой велосипед привязывается неоптимально к чему угодно.

        Для начала нормальный веб-сервер взять. Там нет поддержки делфи? Странно, дайте ка подумать почему? Наверное потому потому что никто не пишет на нем сайты? А если пишет, то, наверное, должен понимать очень хорошо этот инструмент? Иначе в ноги настрелаешь. Но судя по тому что уперлись в раздачу картинок складывается впечатление о не очень хорошем понимании работе http и понимании важности асинхронных файловых операций. Упереться в файловые операции? Не в нагрузку на хостинг? Черт подери, да как это можно сделать специально?)) На винде емнип файловые операции медленнее чем на линухе если бенчмарками заниматься.


        1. harios
          28.09.2021 08:58
          +1

          В данном случае фронтом является uniGui, что совсем "не любой", а тот что позволяет вообще не думать о верстке и веб части в принципе.

          Для начала нормальный веб-сервер взять.

          Зачем? Насколько я понял IIS нужен чисто как файловое хранилище. Если его возможностей достаточно, нужно ли плодить сущности без необходимости? Не думаю.

          Там нет поддержки делфи? Странно, дайте ка подумать почему? Наверное потому потому что никто не пишет на нем сайты?

          Чувствуется не предвзятое и взвешенное мнение. :)


          1. Alexufo
            28.09.2021 13:06

            теперь панатна


      1. PocketM
        28.09.2021 05:46

        Со Java Servlet сдувать пыль не нужно, возьмите Spring Boot. Сейчас — это промышленный стандарт в java


  1. nordfox
    27.09.2021 23:44
    +1

    Апач + мод реврайт + правила в .ht* + простейший скрипт и можно любой элемент сайта засунуть в базу. Хоть весь сайт. Останется только индекс как единая точка входа. Проверено)


    1. nordfox
      27.09.2021 23:54
      +1

      Главное тут, не создавать никаких файлов! Исключаем дисковые операции. Только память. Кеш на уровне СУБД подкрутить. Надо уметь работать с заголовками.

      А если добавить любимый JS Editor, то получим ядро CMS.


  1. korsetlr473
    28.09.2021 01:19
    +1

    посмотрите в сторону minio


  1. Han7
    28.09.2021 01:20
    +13

    Ох уж эти сказочники...

    На практике столкнулся с таким "суперским" хранением.

    Затейник уже давно не работал в компании, а вот БД выросла до 2ТБ, и к моменту моего прихода продолжала рост и как следствие имела жуткие тормоза.

    Реорганизация системы хранения разом избавил от всех этих напастей. Да самих полезных данных в БД осталось 0,5% от первоначальных, остальное стало обычными файлами.


    1. Alexufo
      28.09.2021 05:01
      +2

      Даже по логике бекапы базы важнее бекапов картинок. Точнее частоту бекапов можно делать разной. Чем легче база тем легче производить по ней любые операции.


  1. edo1h
    28.09.2021 01:24
    +2

    В заключение осталось привести новый, значительно облегчённый по сравнению с предыдущим вариантом, код, ибо из БД уже не нужно извлекать само изображение – достаточно лишь получить имя файла с ним (из поля dbo.AlbumCover.name, имеющего в примере псевдоним CoverFileName) и в неизменном виде подставить в атрибут src

    прямо напрашивается сравнение вашего решения с обычными файлами в файловой системе


    1. SergeyPyankov Автор
      28.09.2021 02:05

      Если комментарий относился исключительно к коду, в плане заполнения атрибута при хранении обложек не в БД, а в ФС, то он почти и не станет отличаться от приведённого в конце статьи — ведь суть же как раз в том, что для стороннего наблюдателя файлы из FileTable и выглядят как настоящие файлы из любой реальной директории.

      Если же речь шла про сравнение производительности, то описанное решение даже в самом распрекрасном случае будет лишь на уровне ФС, т. к. она, по определению, и создана для наилучшего обращения с файлами. Статья же затрагивает вариант, когда они по какой-либо причине находятся в базе данных и требуется повысить эффективность работы с ними.


      1. Revertis
        28.09.2021 03:01
        +1

        для стороннего наблюдателя файлы из FileTable и выглядят как настоящие файлы из любой реальной директории

        А зачем это всё?

        Можно ведь просто сделать <img src="get_file.php?id=123456">, а там простым скриптом брать из базы и возвращать (наверно можно и на asp так же). Без всякой записи в файлы и т.п.


        1. Alexufo
          28.09.2021 05:10
          +1

          Пыха мало, нужно думать о ресайзе, оптимизации, нужно ли ресайзить заранее и что делать если сайт изменил дизайн…
          Надо просто хранить картинки нормально на диске, в нормальной структуре с нормальными именами, а в бд хранить или пути к ним или id.
          Отображение делать через прокси какой нибудь imgproxy.net и вопрос каких либо изменений, кеширования, закрыт.


        1. SergeyPyankov Автор
          28.09.2021 12:54

          Скорее всего можно (я не знаком с PHP), но каким бы ни был предложенный способ, он же всё равно будет извлекать изображение из базы данных: Ваш скрипт станет делать это, насколько понимаю, через написанный разработчиком SQL-запрос, а вариант из статьи отдаёт всё на откуп самой СУБД (можно сказать, что условным «запросом» выступает просто имя файла).


          1. Revertis
            28.09.2021 13:02

            1. База закэширует в ОЗУ самые "горячие" файлы.

            2. Скрипт может отдавать файлы через некий прокси, который тоже может кэшить самое горячее, тем самым уменьшив запросы к базе.

            3. Профит.


      1. edo1h
        28.09.2021 07:30
        +1

        Статья же затрагивает вариант, когда они по какой-либо причине находятся в базе данных

        ну вот мне и непонятны эти причины


  1. lam0x86
    28.09.2021 02:06
    +5

    Можно использовать любой S3-совместимый storage или другой тип CDN с нормальным API и нет проблемы с нагрузкой на ФС, заодно и быстрее работать будет, т.к. грузиться будет с ближайшего сервера.


  1. Maizerer
    28.09.2021 02:06
    +3

    А есть ли вообще преимущество при хранении картинок в БД?


    1. Alexufo
      28.09.2021 05:02
      -5

      есть. Вы когда возвращаете id товара возвращаете его фотографию прямо в этом же куске кода. На этом все преимущества заканчиваются))) Если это можно назвать преимуществом.


  1. 1div0
    28.09.2021 03:35

    Прикольно. Я вот не знал про такую технологию у MS SQL сервера. Она вряд ли пригодится, но для общего развития интересно. У нас картинки хранятся на диске. В базе только их имена.


  1. mSnus
    28.09.2021 06:23

    Для этого надо было:

    • использовать PHP

    • хранить картинки в файлах

    • настроить кеш

    • отдавать, если не хочется светить имя файла, через параметр скрипта, без всякой возни с настройкой сервера:

      <img src="covers.php?3AE39458-8925-448A-A5F1-0C0A4524ACF0.jpg">

    Использовать устаревшие и неподходящие технологии -- это как, хм, танцевать в гамаке и в ластах: можно, сложно, вызывает удивление окружающих, но вряд ли кому-то нужно.


  1. ertaquo
    28.09.2021 06:32
    +7

    Статья, конечно, хорошая... Но не нужно так делать ни в коем случае!!!

    База данных - это не файловое хранилище. Если хранить файлы в БД, вы потом задолбаетесь с бэкапами и репликацией данных как минимум. Плюс есть нехилая вероятность, что закончится место на диске и встанет вообще все приложение (а не только загрузка файлов).

    Хотите быстро-просто? Храните описания файлов в базе, а содержимое где-нибудь в S3, благо этот протокол много где поддерживается и не завязан целиком на Amazon (можно взять тот же minio, например).

    А если хотите получить реальную экономию I/O, то лучше смотрите в сторону HTTP заголовков кеширования и ETag.


  1. PaulZi
    28.09.2021 09:07

    А data:uri + base64 нельзя было?


  1. zhulan0v
    28.09.2021 10:14

    Можно было просто cloudflare бесплатный использовать


  1. artem_larin
    28.09.2021 10:35

    Прочитав статью, я так и не понял, так смогли ли дрим театровцы в Six Degress... достигнуть новых вершин, или же предыдущий Metropolis II - это конец, вершина их творчества? В смысле, где же лучше хранить файлы?


    1. SergeyPyankov Автор
      28.09.2021 11:59

      Материал не про выбор места хранения, а про неизвестный многим способ извлечения файлов из БД, когда выбор уже сделан не в пользу ФС. Пример с обложками отчасти даже можно воспринимать лишь как антураж для демонстрации файловых таблиц.


    1. badstarosta
      28.09.2021 13:23

      Несомненно смогли. Второй диск - прекрасен, на мой взгляд. Да и дальше есть хорошие альбомы, как минимум Train Of Thought и Octavarium ещё весьма торты.


  1. mr_bag
    29.09.2021 16:09
    +1

    Обожаю бэкапить базы с картинками, видео и другой фигнёй...