将CLOB写入Oracle中的文件

时间:2018-05-18 18:49:42

标签: oracle plsql oracle11g

我的student_t1表包含一个名为image的blob列。我写了一个base64函数,正确地将BLOB转换为CLOB。现在,我需要将此CLOB写入我的服务器IO_DIR上的/u01/app/oracle/io_dir

执行以下PL / SQL代码时,CLOB将作为文本文件写入目录。我相信整个CLOB都没有输出,因为当我尝试解码时图像被破坏(只有图像的上半部分进入)。我在一个小图像(11k)上运行它,它工作正常。我还监视l_pos正在增加,所以看起来它正在读取CLOB的每个块(32kB)我做错了什么?

我正在使用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 :(得分:0)

首先,你需要dbms_lob.open的BLOB类型变量,所以请改用dbms_lob.open(i.v_clob),在我看来,不要转换为CLOB。

似乎此文件(i.v_clobv_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;
  .....

答案 1 :(得分:0)

尝试使用小于32767的缓冲区,特别是大小不超过8191的缓冲区。

据我所知,如果缓冲区大小超过8191个字符,DBMS_LOB.SUBSTR并不总是表现自己。如果你要求它读取(例如)10000个字符,它可能只能从LOB中读取8191,但它会返回一个10000字符长的字符串,其中前8191个字符是它从LOB读取的内容和另一个字符1809年之前发生的事情(例如,从最后一次致电DBMS_LOB.SUBSTR)。如果缓冲区大小为8191个字符或更小,则不会出现此问题,DBMS_LOB.SUBSTR将返回您要求的字符数。

我无法理解为什么这个函数会这样做。这看起来很奇怪。我只能得出结论,这是数据库中的一个错误。无论是特定于Oracle 11g还是XE,我都不能说。

Oracle documentation for DBMS_LOB.SUBSTR包含以下使用说明中的以下内容,该说明并未解释问题,但确实包含了数字8191:

  
      
  • DBMS_LOB.SUBSTR将根据存储在LOB中的字符返回8191个或更多字符。如果由于字符字节大小超过可用缓冲区而未返回所有字符,则用户应调用带有新偏移量的DBMS_LOB.SUBSTR以读取剩余字符,或者在循环调用子程序直到提取所有数据。
  •   

最后,我的Oracle 11g XE副本使用的是单字节字符集。如果使用多字节字符集,则数字8191可能会更改。

答案 2 :(得分:0)

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;