2.4.2. Работа с файлами

API работы с файлами обеспечивает передачу блобов между сервером и клиентом.

2.4.2.1. Блобы

В разделе затронуты темы сохранения, загрузки и передачи Blob-ов.

Типичный порядок работы
  1. Выполнить запрос паскаль операции
  2. Параметр запроса сохранить в переменную выборки
  3. Переменную выборки сохранить на диск либо в поле выборки
Используется также
  1. Открыть выборку с блоб-полем
  2. Сохранить поле на диск
Порядок работы с большими данными
  1. Выполнить запрос паскаль операции
  2. В параметре запроса вернуть лоб-локатор
  3. Через лоб-локатор сохранить поток на диск

Note

Под “Блоб”-ом понимается одна из трёх сущностей:
  • тип sql-данных - Blob
  • поле выборки куда выбрано поле типа Blob из sql-запроса
  • переменная в скриптере где хранится массив байт

Во всех перечисленных случаях речь идёт о большом блоке данных.

Attention

Большинство методов напрямую работают с блобами и полностью копируют данные в память приложения. Из-за этого при больших объемах велика вероятность нехватки памяти. Решением проблемы работы с большими блобами/файлами является испопльзование лоб-локаторов.

2.4.2.1.1. Блоб в скриптере

Блоб в скриптере - обычная переменная, такая же как переменная хранящая число или строку.

Note

фактический тип данных Блоба в скриптере - Variant array of Byte. В описаниях методов чаще всего описывается просто Variant, реже как Array.

Note

Блобы могут участвовать в работе с запросами и полями выборки, полностью аналогично значениям более простых типов (строки, числа..).

Тип данных БД который соответствует блобу в скриптере - ftOraBlob.

Attention

Каждое присвоение Блоба в скрипотре, или передача не по ссылке, создаёт его копию и всего его содержимого. Если в переменной хранится значительный объём данных, то это чревато возникновением ошибки Out of memory

2.4.2.1.2. Блоб-поле

Работа с блоб-полем, как и с прочими блобами, сводится к загрузке и сохранению данных. Можно сразу работать непосредственно с файлами, или через “Блоб в скриптере”.

See also

Прямые методы работы:
Для работы с использование блобов в скриптере:

Attention

Лучше использовать методы прямой работы. Использовать “блобы в скриптере” при работе с блоб-полями не рекомендуется без необходимости. Этот подход потребляет значительно больше памяти.

Warning

При использовании обычных блобов (поля выборки) в SQL блоке, объявленных как [out] параметры, они имеют тип InputOutput. Если в такой блоб в SQL блоке присвоить знаение NULL, то после выполенния SQL блока может возникнуть ошибка “ORA-22922: nonexistent LOB value”. В таком случае, перед завершением SQL блока необходимо передать в блоб любое значение, кроме NULL и пустой строки, и вызывать DBMS_LOB.FREETEMPORARY(:лоб).

2.4.2.1.3. Примеры

Типичный порядок работы с блобом. Чтение.
<SQL>
  <GetBlob>
  [out blob]
    select bBlob into :blob from TSG_BlobExaple where id = :id;
  </GetBlob>
</SQL>
<PASCAL AFileName>
  V := ExecSQLEx('GetBlob', 'blob', [ftOraBlob], [Null]);
  SaveBlobToFile(AFileName, V[0]);
</PASCAL>
Типичный порядок работы с блобом. Запись.
<SQL>
  <SetBlob>
    update TSG_BlobExaple set bBlob = :blob where id = :id;
  </SetBlob>
</SQL>
<PASCAL AFileName>
  fileContent := LoadFromFileToBlob(AFileName);
  ExecSQLEx('SetBlob', 'blob', [ftOraBlob], [fileContent]);
</PASCAL>
Пример работы с OUT лобом. Порверка на NULL
<SQL>
  <check>
    [out cpRes]
    begin
      if :bpIsPascal = 1 then
        :cpRes := btk_javapascalsqlparser.isCorrectSqlInPascal(:CLOBOPERATION);
      else
        :cpRes := btk_javapascalsqlparser.isCorrectPlSql(:CLOBOPERATION);
      end if;

      if :cpRes is null then
        :cpRes := ' ';
        dbms_lob.FreeTemporary(:cpRes);
      end if;
    end;
  </check>
</SQL>

<PASCAL>
    bvIsPascal := 0;
    v := execSqlEx('check', 'cpRes; bpIsPascal; CLOBOPERATION', [ftOraClob,ftFloat, ftOraClob], [null, bvIsPascal, null]);
</PASCAL>

2.4.2.1.4. Лоб-локатор

Методика работы полностью аналогична работе с обычными параметрами типа ftOraBlob, но в качестве передаваемого значения используется не байтовый массив, а объект класса TbtkScriptOraLobLocator, тип такого параметра следует задавать как ftOraLobLocator.

Note

С помощью локаторов нельзя работать с полями выборки - только с параметрами запроса.

Note

Явным показанием к применению локатора является большой размер файла, тк. он не использует кеширование данных. Отправляет и принимает файл малыми порциями (потоково).

Note

Время жизни lob-локатора управляется ссылками, т.е. его можно передавать из операции в операцию и даже AddVar-ить. Но не стоит забывать о его связи с сессией: не следует использовать локатор созданный в одной сессии в запросах выполняемых в другой.

Attention

При использовании обычных блобов (поля выборки) в SQL блоке, объявленных как [out] параметры, они имеют тип InputOutput. Но при использованиии TbtkScriptOraLobLocator в SQL блоке, обычные блобы, объявленные как [out] параметры, имеют тип Output.

Пример чтения Blob-а с сервера
<SQL>
  <GetBlob>
  [out blob]
    select bBlob into :blob from TSG_BlobExaple where id = :id;
  </GetBlob>
</SQL>
<PASCAL AFileName>
  lob := Selection.CreateLobLocator;
  ExecSQLEx('GetBlob', 'lob', [ftOraLobLocator], [lob]);
  lob.SaveToFile(AFileName);
</PASCAL>
Пример отправки Blob-а на сервер
<SQL>
  <SetBlob>
    update TSG_BlobExaple set bBlob = :blob where id = :id;
  </SetBlob>
</SQL>
<PASCAL AFileName>
  lob := Selection.CreateLobLocator;
  lob.CreateTemporary;
  lob.LoadFromFile(AFileName);
  ExecSQLEx('SetBlob', 'lob', [ftOraLobLocator], [lob]);
</PASCAL>

2.4.2.2. Открытие документа

Запускается стороннее приложение ассоциированное с типом документа в системе. Под документом понимается файл на жёстком диске. Тип документа, как это принято в ОС Windows, соответствует расширению файла. Действие совершенно аналогично двойному клику по файлу ОС Windows.

2.4.2.2.1. Открытие файла с диска

Методы позволяют открывать документы или запускать на выполнение программы. Различаются возможностями печати и ожиданием закрытия файла (работа в модальном режиме).

2.4.2.2.2. Открытие из поля выборки

Поле выборки должно быть типа Blob и содержать документ. Этот блоб сохраняется на диск и дальше происходит открытие файла с диска. Сохранение происходит во временный файл. После того как стороннее приложение будет закрыто клиент Global удалит этот временный файл.

Note

При закрытии приложения Global, если какие-то из открытых документов ещё заняты (открыты в стороннем приложении) - выдаёт соответствующее предупреждение.

2.4.2.3. Диалоги

Диалоги навигации по файловой системе, такие как диалог открытия или сохранения файла, выбора каталога.

2.4.2.4. FAQ

Каким методом узнать, что файл заблокирован?

Проверять файл на доступность перед открытием - плохое решение. Никто не сможет гарантировать, что между проверкой файла на доступность и его открытием не произойдет его блокировка.  Оставлять такие случаи без рассмотрения - дурной тон и ненадежное программирование. Лучшим решением является обработка исключений от метода открытия файла. К сожалению, наш скриптер на данный момент не поддерживает полноценную структурную обработку исключений. Но кое-что придумать всё же можно:

ExecSQL( 'func' );
try
  Selection.BlobOpenEx2('blobe2', 'D:\Temp\foo.doc',  Selection , '' ,wmAuto, true);
except
 //ShowMessage(LastExceptionClassName);
 if SameText(LastExceptionClassName,'EFCreateError') then
  ShowMessage('Кажется что то пошло не так.');
end;

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

<pascal>
  uses System;
  file_ := FileOpen('D:\31.log' ,fmOpenWrite);
  try
    If System.GetLastError <> 0 then
      ShowMessage(SysUtils.SysErrorMessage(System.GetLastError));
  finally
    FileClose(file_);
  end;
</pascal>