Написать CLOB в файл в Oracle - PullRequest
0 голосов
/ 18 мая 2018

моя таблица student_t1 состоит из столбца BLOB-объектов с именем image.Я написал функцию base64, которая правильно конвертирует BLOB в CLOB.Теперь мне нужно записать этот CLOB в мой IO_DIR на моем сервере /u01/app/oracle/io_dir.

Когда выполняется следующий код PL / SQL, CLOB записывается в каталог в виде текстового файла.Я полагаю, что весь CLOB не выводится, хотя, потому что, когда я пытаюсь декодировать, изображение искажается (входит только верхняя часть изображения).Я запустил его на маленьком изображении (11k), и он работал нормально.Я также отслеживал, что l_pos правильно увеличивается, поэтому кажется, что он читает каждый кусок CLOB (32 КБ). Что я делаю не так?

Я использую Oracle 11g Express Edition (XE) и SQL Developer.Вот ошибка и код

create or replace PROCEDURE c2F
 IS
   p_filename       VARCHAR2(100);
   p_dir            VARCHAR2(100) := 'IO_DIR';
   c_amount         CONSTANT BINARY_INTEGER := 32767;
   l_buffer         VARCHAR2(32767);
   l_chr10          PLS_INTEGER;
   l_clobLen        PLS_INTEGER;
   l_fHandler       sys.UTL_FILE.FILE_TYPE;
   l_pos            PLS_INTEGER    := 1;
   v_blob           BLOB;
   p_clob           CLOB;

 BEGIN
  dbms_output.put_line('Start Time: ' || TO_CHAR(sysdate,('YYYY/MM/DD hh24:Mi:ssss')));

  FOR i IN
    (SELECT student_no,
            image v_blob,
            encdec_base64.encode_base64(image) p_clob 
    FROM student_t1
    WHERE student_no =200601022
  ) loop

   IF (dbms_lob.isopen(i.p_clob) = 0) THEN
      dbms_lob.open(i.p_clob,dbms_lob.lob_readonly);
    END IF;
   l_pos      := 1;
   p_filename := i.student_no || '.txt';
   l_clobLen  := DBMS_LOB.GETLENGTH(i.p_clob);
   l_fHandler := sys.UTL_FILE.FOPEN(p_dir, p_fileName,'WB',c_amount);
   l_buffer := DBMS_LOB.SUBSTR(i.p_clob, c_amount, l_pos);

  WHILE l_pos < l_clobLen LOOP
     l_buffer := DBMS_LOB.SUBSTR(i.p_clob, c_amount, l_pos);

     EXIT WHEN l_buffer IS NULL;
     UTL_FILE.put_raw(l_fHandler,utl_raw.cast_to_raw(l_buffer));
     l_pos := l_pos + LEAST(LENGTH(l_buffer)+1,c_amount);
     UTL_FILE.FFLUSH(l_fHandler);
     END LOOP;
  END LOOP;
   sys.UTL_FILE.FCLOSE(l_fHandler);

 EXCEPTION
  WHEN OTHERS THEN
   IF UTL_FILE.IS_OPEN(l_fHandler) THEN
   UTL_FILE.FCLOSE(l_fHandler);
   END IF;
   RAISE;
dbms_output.put_line('End Time: ' || TO_CHAR(sysdate,('YYYY/MM/DD hh24:Mi:ssss')));
 END; 

Ответы [ 3 ]

0 голосов
/ 19 мая 2018

Попробуйте использовать меньший размер буфера, чем 32767, в частности, размер буфера не более 8191.

Насколько я могу судить, DBMS_LOB.SUBSTR не всегда ведет себя само, если размер буфера превышает 8191персонажи.Если вы попросите его прочитать (скажем) 10000 символов, он может прочитать только 8191 из большого объекта, но вернет вам строку длиной 10000 символов, где первые 8191 символов были теми, которые были прочитаны из большого объекта, а другие1809 было то, что случилось там раньше (например, с последнего звонка на DBMS_LOB.SUBSTR).Если размер буфера составляет 8191 символ или меньше, эта проблема не возникает, и DBMS_LOB.SUBSTR возвращает количество запрашиваемых символов.

Я не могу понять, почему эта функция будет делать это.Это кажется очень странным.Я могу только сделать вывод, что это ошибка в базе данных.Я не могу сказать, специфичен ли он для Oracle 11g или XE.

Документация Oracle для DBMS_LOB.SUBSTR включает в себя примечания по использованию, которые не объясняют проблемуно включает упоминание числа 8191:

  • DBMS_LOB.SUBSTR вернет 8191 или более символов на основе символов, хранящихся в больших объектах.Если все символы не возвращаются вследствие того, что размер байта символа превышает доступный буфер, пользователь должен либо вызвать DBMS_LOB.SUBSTR с новым смещением для чтения оставшихся символов, либо вызвать подпрограмму в цикле, пока все данные не будут извлечены.

Наконец, моя копия Oracle 11g XE использует однобайтовый набор символов.Число 8191 может измениться, если вы используете многобайтовый набор символов.

0 голосов
/ 22 мая 2018
create or replace PROCEDURE CONVERT_CLOB_2_FILE(
    p_fileName IN VARCHAR2,
    p_dir      IN VARCHAR2,
    p_clob     IN CLOB )
AS
  v_lob_image_id NUMBER;
  v_clob CLOB := p_clob;
  v_buffer RAW (32767);
  c_buffer VARCHAR2 (32767);
  v_buffer_size BINARY_INTEGER;
  v_amount BINARY_INTEGER;
  v_pos       NUMBER (38) := 1;
  v_clob_size INTEGER;
  v_out_file UTL_FILE.file_type;
BEGIN
  --dbms_output.put_line('Start Time: ' || TO_CHAR(sysdate,('YYYY/MM/DD hh24:Mi:ssss')));
  v_pos       := 1;
  v_clob_size := DBMS_LOB.GETLENGTH (v_clob);
  --IF (v_clob_size  < 32767) THEN
  -- v_buffer_size := v_clob_size;
  -- ELSE
  -- v_buffer_size := 32767;
  --END IF;
  v_buffer_size              := 32767;
  v_amount                   := v_buffer_size;
  IF (dbms_lob.isopen(v_clob) = 0) THEN
    dbms_lob.open(v_clob,dbms_lob.lob_readonly);
  END IF;
  v_out_file     := UTL_FILE.fopen (p_dir,p_fileName , 'WB', max_linesize => 32767);
  WHILE v_amount >= v_buffer_size
  LOOP
    DBMS_LOB.read (v_clob, v_amount, v_pos, c_buffer);
    --c_buffer := DBMS_LOB.SUBSTR(v_clob, v_amount, v_pos);
    v_buffer := UTL_RAW.CAST_TO_RAW(c_buffer);
    v_pos    := v_pos + v_amount;
    UTL_FILE.put_raw (v_out_file, v_buffer, TRUE);
    UTL_FILE.fflush (v_out_file);
  END LOOP;
  UTL_FILE.fflush (v_out_file);
  UTL_FILE.fclose (v_out_file);
  IF ( dbms_lob.isopen(v_clob) = 1 ) THEN
    dbms_lob.close(v_clob);
  END IF;
  --dbms_output.put_line('End Time: ' || TO_CHAR(sysdate,('YYYY/MM/DD hh24:Mi:ssss')));
EXCEPTION
WHEN OTHERS THEN
  IF ( dbms_lob.isopen(v_clob) = 1 ) THEN
    dbms_lob.close(v_clob);
  END IF;
  RAISE;
END;
0 голосов
/ 19 мая 2018

Прежде всего вам нужна переменная типа BLOB для dbms_lob.open, поэтому используйте вместо нее dbms_lob.open(i.v_clob), и, по моему мнению, не конвертируйте в CLOB.

Кажется, этот файл (i.v_clob или v_clob) должен быть закрыт где-то внизу, может быть так:

  ....
  WHILE l_pos < l_clobLen LOOP
     l_buffer := DBMS_LOB.SUBSTR(i.p_clob, c_amount, l_pos);

     EXIT WHEN l_buffer IS NULL;
     .....
     UTL_FILE.FFLUSH(l_fHandler);
     dbms_lob.close(i.v_clob); 
     v_clob := i.v_clob;
     END LOOP;
  END LOOP;
   sys.UTL_FILE.FCLOSE(l_fHandler);

 EXCEPTION
  WHEN OTHERS THEN
   IF UTL_FILE.IS_OPEN(l_fHandler) THEN
   UTL_FILE.FCLOSE(l_fHandler);
   dbms_lob.close(v_clob); 
   END IF;
  .....
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...