【问题标题】:Base64 encoding and decoding in oracleoracle中base64编码和解码
【发布时间】:2017-11-03 03:46:48
【问题描述】:

如何在 Oracle 中对值进行 Base64 编码/解码?

【问题讨论】:

    标签: oracle base64


    【解决方案1】:

    我已经实现了这一点,以便通过我的 MS Exchange 服务器发送西里尔文电子邮件。

    function to_base64(t in varchar2) return varchar2 is
     begin
        return utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(t)));
    end to_base64;
    

    试试看。

    upd:经过一个小的调整,我想出了这个,所以它现在可以双向工作了:

    function from_base64(t in varchar2) return varchar2 is
    begin
      return utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(t)));
    end from_base64;
    

    你可以检查一下:

    SQL> set serveroutput on
    SQL> 
    SQL> declare
      2    function to_base64(t in varchar2) return varchar2 is
      3    begin
      4      return utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(t)));
      5    end to_base64;
      6  
      7    function from_base64(t in varchar2) return varchar2 is
      8    begin
      9      return utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw    (t)));
     10    end from_base64;
     11  
     12  begin
     13    dbms_output.put_line(from_base64(to_base64('asdf')));
     14  end;
     15  /
    
    asdf
    
    PL/SQL procedure successfully completed
    

    upd2:好的,这是我刚刚想出的适用于CLOB 的转换示例。尝试为您的 blob 解决问题。 :)

    declare
    
      clobOriginal     clob;
      clobInBase64     clob;
      substring        varchar2(2000);
      n                pls_integer := 0;
      substring_length pls_integer := 2000;
    
      function to_base64(t in varchar2) return varchar2 is
      begin
        return utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(t)));
      end to_base64;
    
      function from_base64(t in varchar2) return varchar2 is
      begin
        return utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(t)));
      end from_base64;
    
    begin
    
      select clobField into clobOriginal from clobTable where id = 1;
    
      while true loop
    
        /*we substract pieces of substring_length*/
        substring := dbms_lob.substr(clobOriginal,
                                     least(substring_length, substring_length * n + 1 - length(clobOriginal)),
                                     substring_length * n + 1);  
        /*if no substring is found  - then we've reached the end of blob*/
    
        if substring is null then
          exit;
        end if;  
    
        /*convert them to base64 encoding and stack it in new clob vadriable*/
        clobInBase64 := clobInBase64 || to_base64(substring);          
        n := n + 1;  
    
      end loop;
    
      n := 0;
      clobOriginal := null;
    
      /*then we do the very same thing backwards - decode base64*/
      while true loop 
    
        substring := dbms_lob.substr(clobInBase64,
                                     least(substring_length, substring_length * n + 1 - length(clobInBase64)),
                                     substring_length * n + 1);  
        if substring is null then
          exit;
        end if;  
        clobOriginal := clobOriginal || from_base64(substring);  
        n := n + 1;  
      end loop; 
    
          /*and insert the data in our sample table - to ensure it's the same*/
      insert into clobTable (id, anotherClobField) values (1, clobOriginal);
    
    end;
    

    【讨论】:

    • 对我来说效果很好,尽管我的编码中间有空格。我在结果周围放置了一个正则表达式空格替换,我在做生意: regexp_replace(utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(t))), '[[:space:]]*','')
    • 第二次更新有个小bug:dbms_log.substr 的“amount”参数应该是“least(substring_length, length(clobInBase64) - (substring_length * n + 1) )”。您编写它的方式返回负数,因此子字符串始终为空。
    • BASE64 编码需要 3 字节的输入流并将其转换为 4 * 3 字节。这 3 个字节中的每一个都映射到 64 个不同的字符(a-z、A-Z、0-9、“+”、“/”——因此您得到名称 BASE64)。对于BASE64_DECODE,确保 substring_length 的值是 4 的整数倍。 BASE64_ENCODE 是 3 的整数倍。因此您的to_base64 函数可能会返回错误的结果。
    • 每 64 个字符后我得到 '\r\n',我该如何删除它们??
    【解决方案2】:

    utl_encode.base64_encodeutl_encode.base64_decode 的解决方案有一个限制,它们仅适用于最多 32,767 个字符/字节的字符串。

    如果您必须转换更大的字符串,您将面临一些障碍。

    • 对于BASE64_ENCODE,该函数必须读取 3 个字节 并对其进行转换。如果是多字节字符(例如,öäüè€ 以 UTF-8 存储,又名 AL32UTF8),3 Character 不一定也是 3 Bytes。为了始终读取 3 字节,您必须先将 CLOB 转换为 BLOB
    • 同样的问题适用于BASE64_DECODE。该函数必须读取 4 个字节并将它们转换为 3 个字节。这 3 个 Bytes 不一定也是 3 个 Characters
    • 通常 BASE64 字符串具有 NEW_LINE(CR 和/或 LF)字符,每 64 个字符。解码时必须忽略此类换行符。

    考虑到所有这些,功能齐全的解决方案可能是这个:

    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 := DBMS_LOB.GETLENGTH(InBase64Char);
    
        amount INTEGER := 1440; -- must be a whole multiple of 4
        buffer RAW(1440);
        stringBuffer VARCHAR2(1440);
        -- BASE64 characters are always simple ASCII. Thus you get never any Mulit-Byte character and having the same size as 'amount' is sufficient
    
    BEGIN
    
        IF InBase64Char IS NULL OR NVL(ClobLen, 0) = 0 THEN 
            RETURN NULL;
        ELSIF ClobLen<= 32000 THEN
            RETURN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_DECODE(UTL_RAW.CAST_TO_RAW(InBase64Char)));
        END IF;        
        -- UTL_ENCODE.BASE64_DECODE is limited to 32k, process in chunks if bigger    
    
        -- 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;
        ClobLen INTEGER := DBMS_LOB.GETLENGTH(InClearChar);
    
        amount INTEGER := 1440; -- must be a whole multiple of 3
        -- size of a whole multiple of 48 is beneficial to get NEW_LINE after each 64 characters 
        buffer RAW(1440);
        res CLOB := EMPTY_CLOB();
    
    BEGIN
    
        IF InClearChar IS NULL OR NVL(ClobLen, 0) = 0 THEN 
            RETURN NULL;
        ELSIF ClobLen <= 24000 THEN
            RETURN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(InClearChar)));
        END IF;
        -- UTL_ENCODE.BASE64_ENCODE is limited to 32k/(3/4), process in chunks if bigger    
    
        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;
    

    【讨论】:

      【解决方案3】:

      之前所有的帖子都是正确的。给猫剥皮的方法不止一种。这是做同样事情的另一种方法:(只需将“what_ever_you_want_to_convert”替换为您的字符串并在 Oracle 中运行:

          set serveroutput on;
          DECLARE
          v_str VARCHAR2(1000);
          BEGIN
          --Create encoded value
          v_str := utl_encode.text_encode
          ('what_ever_you_want_to_convert','WE8ISO8859P1', UTL_ENCODE.BASE64);
          dbms_output.put_line(v_str);
          --Decode the value..
          v_str := utl_encode.text_decode
          (v_str,'WE8ISO8859P1', UTL_ENCODE.BASE64);
          dbms_output.put_line(v_str);
          END;
          /
      

      source

      【讨论】:

        【解决方案4】:

        在 oracle 6 中支持 url_raw.cast_to_raw()

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-05-18
          • 1970-01-01
          • 2015-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多