我的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;
答案 0 :(得分:0)
首先,你需要dbms_lob.open
的BLOB类型变量,所以请改用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;
.....
答案 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;