You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
/

关键细节

  1. UTL_ZIP包依赖:确保数据库用户拥有UTL_ZIP包的执行权限,若没有可通过GRANT EXECUTE ON UTL_ZIP TO your_user;授权。
  2. 字符集匹配:read_text中的charset参数需与Java压缩时的字符集一致(示例中为UTF-8)。
  3. 大内容处理:通过循环read_text确保能读取超过32767字节的大XML内容。
  4. 资源释放:在异常块中添加资源清理逻辑,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Nila

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:07:29