【问题标题】:Write a CLOB to File in Oracle在 Oracle 中将 CLOB 写入文件
【发布时间】:2018-10-29 06:21:36
【问题描述】:

我的 student_t1 表由一个名为 image 的 blob 列组成。我编写了一个 base64 函数,可以正确地将 BLOB 转换为 CLOB。现在,我需要将此 CLOB 写入我的服务器 /u01/app/oracle/io_dir 上的 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; 

【问题讨论】:

    标签: oracle plsql oracle11g


    【解决方案1】:

    首先,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;
      .....
    

    【讨论】:

      【解决方案2】:

      尝试使用小于 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 可能会发生变化。

      【讨论】:

      • 以下对缓冲区的修改改变了问题。感谢您的回复。
      【解决方案3】:
      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;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-06
        • 2020-11-10
        • 2010-09-06
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        相关资源
        最近更新 更多