解码大base64编码的blob

时间:2016-11-25 08:59:39

标签: oracle plsql base64

我有一个大小约为3.5mb的xml文件。我将它包围并传播到我需要解码的另一个数据库。我在迭代中对它进行解码,因为它太大而无法一次解码,但是在某些迭代中它无法解码,我得到了一些细节。我相信它是因为一些符号可以是1个字节而其他符号是2个字节,有时子串将2个字节的一个符号切成两半,并且该迭代将出现在giberish中。我已经想过我可以尝试将每个子字符串转换为clob,因为它在转换失败时会出现警告,字符和警告出现时,会将子字符串中的数量增加一些数字,直到我还没有以这种方式成功解码。有没有解决方法呢?

更新

成功解码,并进行警告检查。您需要做的就是尝试将子字符串转换为clob,dbms_lob.convertblobtoclob检查warning != 0,如果是,请将偏移量减少1并转到下一次迭代,而不将子字符串写入blob。然而,这是非常记忆的costy检查,因为它需要创建blob并将该blob转换为clob。有没有更简单的解决方法,也许我错过了一些非常明显的东西?

更新

我有一个xml文件,其中包含以base64编码的付款xml,以及有关该xml的其他数据。 f.e。

<envelope>
  <file_name>a.xml</file_name>
  <...><...>
  <data>BASE64 ENCODED XML FILE</data>
</envelope>

完整的脚本,几个例子。早些时候我已经提到了警告检查的解决方法,但在这个例子中它似乎不起作用,当我更好地考虑它时,它不应该。无论如何,这里的脚本:

declare
  l_clob clob := empty_clob();

  function convert_clob_to_blob(
    p_clob in clob) return blob is

    l_dest_offsset number := 1;
    l_src_offsset number := 1;
    l_lang_context number := dbms_lob.default_lang_ctx;
    l_warning number;

    l_result blob;
  begin
    dbms_lob.createtemporary(l_result, false);
    dbms_lob.convertToBlob(
      dest_lob => l_result,
      src_clob => p_clob,
      amount => dbms_lob.lobmaxsize,
      dest_offset => l_dest_offsset,
      src_offset => l_src_offsset,
      blob_csid => dbms_lob.default_csid,
      lang_context => l_lang_context,
      warning => l_warning);

    if l_warning != 0 then
      raise_application_error(-20001, 'sd' || '.convert_blob_to_clob ' || l_warning);
    end if;
    return l_result;
  end;

  function gen_rand_xml return clob is

    l_xml xmltype := xmltype('<envelope><nullnode></nullnode></envelope>');
  begin
    for i in 1..50 loop
        SELECT
          insertXMLafter(
            l_xml,
            '/envelope/nullnode',
            XMLType('<node>' || i || '</node>'))
        INTO
          l_xml
        FROM dual;
    end loop;

    return l_xml.getClobVal();
  end;

  function to_base64(
    p_clob in clob) return clob is

    l_length integer;
    l_offset integer := 1;
    l_amt binary_integer := 600;
    l_buffer varchar2(1800);

    l_result clob := empty_clob();
    l_temp_blob blob;
  begin
    dbms_lob.createtemporary(l_temp_blob, false);
    l_temp_blob := convert_clob_to_blob(p_clob);

    l_length := dbms_lob.getlength(l_temp_blob);
    while l_offset < l_length loop
      l_result := l_result || utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(l_temp_blob, l_amt, l_offset)));
      l_offset := l_offset + l_amt;
    end loop;

    return l_result;
  end;

  function from_base64(
    p_clob in clob) return clob is

    l_length integer := dbms_lob.getLength(p_clob);
    l_offset integer := 1;
    l_amt binary_integer := 800;
    l_buffer varchar2(3200);

    l_result clob := empty_clob();
  begin  

    while l_offset <= l_length loop
      l_buffer := replace(replace(dbms_lob.substr(p_clob, l_amt, l_offset), chr(10), null), chr(13), null);
      l_offset := l_offset + l_amt;
      while l_offset <= l_length and mod(dbms_lob.getLength(l_buffer), 4) > 0 loop
        l_buffer := l_buffer || replace(replace(dbms_lob.substr(p_clob, 1, l_offset), chr(10), null), chr(13), null);
        l_offset := l_offset + 1;
      end loop;
      l_result := l_result || utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(l_buffer)));
    end loop;

    return l_result;
  end;

  procedure print_clob( p_clob in clob )
  as
    l_offset number default 1;
  begin
    loop
    exit when l_offset > dbms_lob.getlength(p_clob);
    dbms_output.put_line( dbms_lob.substr( p_clob, 4000, l_offset ) );
    l_offset := l_offset + 4000;
    end loop;
  end;
begin
  l_clob := gen_rand_xml;
  print_clob(from_base64(to_base64(l_clob)));
end;
/

1 个答案:

答案 0 :(得分:1)

我仍然不明白你的意思,大多数我都不明白你为什么要从BLOB转换为CLOB。

无论如何,我的应用程序中有类似的情况,在XML文件中使用BASE64编码的XML。

对于编码和解码,我使用这些功能,这可能对您的情况有所帮助。

CREATE OR REPLACE FUNCTION DecodeBASE64(InBase64Char IN OUT NOCOPY CLOB) RETURN CLOB IS

    blob_loc BLOB;
    clob_trim CLOB;
    res CLOB;

    lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
    dest_offset INTEGER := 1;
    src_offset INTEGER := 1;
    read_offset INTEGER := 1;
    warning INTEGER;
    ClobLen INTEGER;

    amount INTEGER := 1440; -- must be a whole multiple of 4
    buffer RAW(1440);
    stringBuffer VARCHAR2(1440);

BEGIN

    -- Remove all NEW_LINE from base64 string
    ClobLen := DBMS_LOB.GETLENGTH(InBase64Char);
    DBMS_LOB.CREATETEMPORARY(clob_trim, TRUE);
    LOOP
        EXIT WHEN read_offset > ClobLen;
        stringBuffer := REPLACE(REPLACE(DBMS_LOB.SUBSTR(InBase64Char, amount, read_offset), CHR(13), NULL), CHR(10), NULL);
        DBMS_LOB.WRITEAPPEND(clob_trim, LENGTH(stringBuffer), stringBuffer);
        read_offset := read_offset + amount;
    END LOOP;

    read_offset := 1;
    ClobLen := DBMS_LOB.GETLENGTH(clob_trim);
    DBMS_LOB.CREATETEMPORARY(blob_loc, TRUE);
    LOOP
        EXIT WHEN read_offset > ClobLen;
        buffer := UTL_ENCODE.BASE64_DECODE(UTL_RAW.CAST_TO_RAW(DBMS_LOB.SUBSTR(clob_trim, amount, read_offset)));
        DBMS_LOB.WRITEAPPEND(blob_loc, DBMS_LOB.GETLENGTH(buffer), buffer);
        read_offset := read_offset + amount;
    END LOOP;

    DBMS_LOB.CREATETEMPORARY(res, TRUE);
    DBMS_LOB.CONVERTTOCLOB(res, blob_loc, DBMS_LOB.LOBMAXSIZE, dest_offset, src_offset,  DBMS_LOB.DEFAULT_CSID, lang_context, warning);

    DBMS_LOB.FREETEMPORARY(blob_loc);
    DBMS_LOB.FREETEMPORARY(clob_trim);
    RETURN res;


END DecodeBASE64;




CREATE OR REPLACE FUNCTION EncodeBASE64(InClearChar IN OUT NOCOPY CLOB) RETURN CLOB IS

    dest_lob BLOB;  
    lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
    dest_offset INTEGER := 1;
    src_offset INTEGER := 1;
    read_offset INTEGER := 1;
    warning INTEGER;

    amount INTEGER := 1440; -- must be a whole multiple of 3
    buffer RAW(1440);
    res CLOB := EMPTY_CLOB();

BEGIN

    IF DBMS_LOB.GETLENGTH(InClearChar) IS NULL THEN 
        RETURN NULL;
    END IF;

    DBMS_LOB.CREATETEMPORARY(dest_lob, TRUE);
    DBMS_LOB.CONVERTTOBLOB(dest_lob, InClearChar, DBMS_LOB.LOBMAXSIZE, dest_offset, src_offset, DBMS_LOB.DEFAULT_CSID, lang_context, warning);
    LOOP
        EXIT WHEN read_offset >= dest_offset;
        DBMS_LOB.READ(dest_lob, amount, read_offset, buffer);
        res := res || UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(buffer));       
        read_offset := read_offset + amount;
    END LOOP;
    DBMS_LOB.FREETEMPORARY(dest_lob);
    RETURN res;


END EncodeBASE64;

您可以像这样使用

DECLARE
    str VARCHAR2(1000) := '<envelope><file_name>a.xml</file_name><data>some text</data></envelope>';
    base64 VARCHAR2(1000);
BEGIN
    base64 := EncodeBASE64(str);
    DBMS_OUTPUT.PUT_LINE( base64 );

    str :=  DecodeBASE64(base64);
    DBMS_OUTPUT.PUT_LINE( str );

END;

输出:

PGVudmVsb3BlPjxmaWxlX25hbWU+YS54bWw8L2ZpbGVfbmFtZT48ZGF0YT5zb21l
IHRleHQ8L2RhdGE+PC9lbnZlbG9wZT4=
<envelope><file_name>a.xml</file_name><data>some text</data></envelope>

也许这个例子更接近你的用例:

DECLARE
    payment CLOB := '<payment><amout>50 Cent</amout><recipient>Wernfried Domscheit</recipient></payment>';
    envelope XMLTYPE;
    base64 CLOB;

BEGIN

    SELECT 
        XMLELEMENT("envelope", 
            XMLELEMENT("file_name", 'a.xml'),
            XMLELEMENT("data", EncodeBASE64(payment))
        )
    INTO envelope
    FROM dual;
    DBMS_OUTPUT.PUT_LINE( envelope.getclobval() || CHR(13));

    SELECT RETURN_BASE64
    INTO base64
    FROM XMLTABLE('envelope/data' PASSING envelope COLUMNS
        RETURN_BASE64 CLOB PATH '/');       
    DBMS_OUTPUT.PUT_LINE( base64 || CHR(13) );

    payment := DecodeBASE64(base64);
    DBMS_OUTPUT.PUT_LINE( payment );

END;

输出:

<envelope><file_name>a.xml</file_name><data>PHBheW1lbnQ+PGFtb3V0PjUwIENlbnQ8L2Ftb3V0PjxyZWNpcGllbnQ+V2VybmZy
aWVkIERvbXNjaGVpdDwvcmVjaXBpZW50PjwvcGF5bWVudD4=</data></envelope>

PHBheW1lbnQ+PGFtb3V0PjUwIENlbnQ8L2Ftb3V0PjxyZWNpcGllbnQ+V2VybmZy
aWVkIERvbXNjaGVpdDwvcmVjaXBpZW50PjwvcGF5bWVudD4=

<payment><amout>50 Cent</amout><recipient>Wernfried Domscheit</recipient></payment>

为了不浪费内存,在使用LOB时应始终使用IN OUT NOCOPY子句。这样就不会复制LOB进行过程调用,只需传递一个指针即可。