Oracle存储过程:将BLOB中的压缩XML转换为XML文件
问题:Oracle存储过程还原Java压缩的ZIP格式BLOB为XML
问题背景
Java端将XML字符串通过ZipOutputStream压缩为ZIP字节数组,存入Oracle数据库的BLOB字段,并且可以通过ZipInputStream正常读取还原。但尝试用Oracle存储过程(如lz_uncompress)还原时出现报错,需要正确的实现方案。
Java压缩存入代码
ByteArrayOutputStream byteOut = null; ZipOutputStream out = null; try { byteOut = new ByteArrayOutputStream(); out = new ZipOutputStream(byteOut); out.setLevel(level); ZipInput entry = new ZipStringInput("string", xmlString); out.putNextEntry(getZipEntry(encrypt, entry.getEntryName())); InputStream in = entry.getInputStream(); byte[] buffer = new byte[BUFFER]; for (int length; (length = in.read(buffer)) != -1; ) { out.write(buffer, 0, length); } out.finish(); saveToDB(byteOut.toByteArray()); } // 省略异常处理和资源关闭代码
Java读取还原代码
byte[] xmlBytes = (byte[])blob; ByteArrayOutputStream out = new ByteArrayOutputStream(); try (ByteArrayInputStream byteIn = new ByteArrayInputStream(xmlBytes);) { try (ZipInputStream zipIn = new ZipInputStream(byteIn);) { for (ZipEntry entry; (entry = zipIn.getNextEntry()) != null;) { byte[] buffer = new byte[32768]; for (int length; (length = zipIn.read(buffer, 0, buffer.length)) != -1;) { out.write(buffer, 0, length); } } return out.toString("UTF-8"); } } // 省略异常处理代码
尝试的Oracle存储过程(报错)
CREATE OR REPLACE PROCEDURE print_clob AS v_clob CLOB; v_varchar VARCHAR2(32767); v_start PLS_INTEGER := 1; v_buffer PLS_INTEGER := 32767; blob_in BLOB; x XMLTYPE; BEGIN dbms_lob.createtemporary(v_clob, true); FOR i IN ( SELECT blobdata AS blob_in FROM myTable WHERE id = 123 ) LOOP FOR j IN 1..ceil(dbms_lob.getlength(i.blob_in) / v_buffer) LOOP v_varchar := dbms_lob.substr(i.blob_in, v_buffer, v_start); dbms_lob.writeappend(v_clob, length(v_varchar), v_varchar); v_start := v_start + v_buffer; END LOOP; dbms_output.put_line(v_clob ); -- 输出类似'504B0304140008000800E0843E4200000000000000000.....'的二进制字符串 -- dbms_output.put_line(UTL_RAW.CAST_TO_NVARCHAR2(v_clob )); -> 报错 -- dbms_output.put_line(utl_compress.lz_uncompress(v_clob )); -> 报错 --x := xmltype.createxml(v_clob); -> 报错,提示输入不是XML -- dbms_output.put_line(x.getclobval()); END LOOP; END;
正确实现方案
核心说明
Java使用的是标准ZIP格式压缩(包含文件元数据、压缩算法标识等),utl_compress.lz_uncompress仅适用于LZ系列压缩算法,无法解析ZIP格式。需要使用Oracle官方提供的UTL_ZIP包来解析ZIP格式的BLOB数据。
完整存储过程代码
CREATE OR REPLACE PROCEDURE extract_xml_from_zip_blob AS v_blob BLOB; v_zip_handle UTL_ZIP.ZIP_FILE_HANDLE; v_entries UTL_ZIP.ZIP_FILE_ENTIES; v_entry UTL_ZIP.ZIP_FILE_ENTRY; v_clob CLOB; v_buffer VARCHAR2(32767); v_amount PLS_INTEGER := 32767; v_offset PLS_INTEGER := 1; BEGIN -- 读取目标BLOB数据 SELECT blobdata INTO v_blob FROM myTable WHERE id = 123; -- 打开ZIP格式的BLOB流 UTL_ZIP.open_zip( zip_file => v_blob, zip_handle => v_zip_handle, charset => 'UTF8' ); -- 获取ZIP内的所有文件条目(Java仅存入一个,取第一个即可) v_entries := UTL_ZIP.get_entries(v_zip_handle); v_entry := v_entries(1); -- 创建临时CLOB用于存储解析后的XML内容 DBMS_LOB.createtemporary(v_clob, TRUE); -- 读取ZIP条目内容到CLOB(循环读取大内容) LOOP BEGIN UTL_ZIP.read_text( zip_handle => v_zip_handle, entry_name => v_entry.name, buffer => v_buffer, amount => v_amount, offset => v_offset, charset => 'UTF8' ); DBMS_LOB.writeappend(v_clob, LENGTH(v_buffer), v_buffer); v_offset := v_offset + v_amount; EXCEPTION WHEN NO_DATA_FOUND THEN EXIT; -- 读取完毕退出循环 END; END LOOP; -- 输出解析后的XML内容 DBMS_OUTPUT.put_line(v_clob); -- 清理资源 UTL_ZIP.close_zip(v_zip_handle); DBMS_LOB.freetemporary(v_clob); EXCEPTION WHEN OTHERS THEN -- 异常时确保资源释放 IF UTL_ZIP.is_open(v_zip_handle) THEN UTL_ZIP.close_zip(v_zip_handle); END IF; IF DBMS_LOB.istemporary(v_clob) THEN DBMS_LOB.freetemporary(v_clob); END IF; RAISE; -- 抛出原异常便于排查 END; /
关键细节
- UTL_ZIP包依赖:确保数据库用户拥有
UTL_ZIP包的执行权限,若没有可通过GRANT EXECUTE ON UTL_ZIP TO your_user;授权。 - 字符集匹配:
read_text中的charset参数需与Java压缩时的字符集一致(示例中为UTF-8)。 - 大内容处理:通过循环
read_text确保能读取超过32767字节的大XML内容。 - 资源释放:在异常块中添加资源清理逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Nila
相关产品推荐
相关产品推荐

