2010-09-27 2 views

risposta

41

Ho implementato questo per inviare cirillici e-mail attraverso il mio server 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; 

Provalo.

UPD: dopo un aggiustamento minore sono arrivato fino a questo, in modo che funziona in entrambi i sensi ora:

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; 

è possibile controllare:

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: Ok, Ecco una conversione di esempio che funziona per CLOB Mi è appena venuto in mente. Prova a risolverlo per i tuoi 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; 
+1

funziona abbastanza bene per me anche se ottengo spazi nel mezzo della mia codifica. Inserisco uno spazio di espressione regolare in sostituzione del mio risultato e sono in affari: regexp_replace (utl_raw.cast_to_varchar2 (utl_encode.base64_encode (utl_raw.cast_to_raw (t))), '[[: space:]] *', '') – Kirby

+2

C'è un piccolo bug nel secondo aggiornamento: il parametro "amount" del dbms_log.substr dovrebbe essere "least (substring_length, length (clobInBase64) - (substring_length * n + 1))". Il modo in cui lo hai scritto restituisce una quantità negativa, quindi la sottostringa è sempre vuota. – BernardMarx

+1

La codifica BASE64 richiede 3 byte di flusso di input e la converte in 4 * 3 byte. Ognuno di questi 3 byte viene mappato su 64 caratteri diversi (a-z, A-Z, 0-9, "+", "/" - quindi si ottiene il nome BASE64). Assicurati che il valore di substring_length sia un multiplo intero di 4 per 'BASE64_DECODE', risp. un intero multiplo di 3 per 'BASE64_ENCODE'. Quindi la tua funzione 'to_base64' potrebbe restituire risultati errati. –

5
+0

può u prego di dirmi come fare per il tipo di dati BLOB? Ricevo un'eccezione "Bugger Troppo piccolo per la conversione da CLOB a CHAR o da BLOB a RAW (effettivo 50176, massimo 2000)". – Pradeep

+1

Scusa ... Ricevo un'eccezione "BufferToo small per la conversione da CLOB a CHAR o da BLOB a RAW (effettivo 50176, massimo 2000)". – Pradeep

+0

oracle utl_raw pacchetto accetta solo input varchar2, che è breve, quindi immagino che tutto ciò che potresti fare sia dividere il tuo BLOB in blocchi più piccoli usando il pacchetto dbms_lob, quindi convertirli ciascuno usando il metodo che ho fornito, e poi rimetterli insieme indietro in un singolo blob. puoi dare un codice di errore che stai ricevendo, a proposito? –

0

Tutti i post precedenti sono corretti. C'è più di un modo per dare la pelle a un gatto. Ecco un altro modo per fare la stessa cosa: (basta sostituire "what_ever_you_want_to_convert" con la stringa ed eseguirlo in 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

-1

fare url_raw.cast_to_raw() supporto in Oracle 6

1

Soluzione con utl_encode.base64_encode e utl_encode.base64_decode hanno una limitazione, funzionano solo con stringhe fino a 32.767 caratteri/byte.

Nel caso in cui si debbano convertire stringhe più grandi si dovranno affrontare diverse o bstacles.

  • Per BASE64_ENCODE la funzione è di leggere 3 Bytes e trasformarli. In caso di caratteri Multi-Byte (ad esempio öäüè€ memorizzati in UTF-8, ovvero AL32UTF8) 3 Il carattere non è necessariamente anche 3 Byte. Per leggere sempre 3 byte devi convertire il tuo CLOB in BLOB prima.
  • Lo stesso problema si applica a BASE64_DECODE. La funzione deve leggere 4 byte e trasformarli in 3 byte. Quei 3 Bytes non sono necessariamente anche 3 caratteri
  • Tipicamente un BASE64-String ha NEW_LINE (CR e/o LF) carattere ciascuna 64 caratteri. Tali caratteri di nuova riga devono essere ignorati durante la decodifica.

Prendendo tutto questo in considerazione la soluzione piena funzionalità potrebbe essere questa:

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;