2010-09-27 66 views

Respuesta

41

He implementado esto para enviar correos electrónicos a través cirílico mi servidor 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; 

Pruébalo.

UPD: después de un ajuste menor que se me ocurrió esto, por lo que funciona en ambos sentidos ahora:

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; 

se puede comprobar:

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, Aquí hay una conversión de muestra que funciona para CLOB. Solo se me ocurrió. Trata de resolverlo para tus blobs. :)

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

me funciona bastante bien, aunque tengo espacios en medio de mi codificación. Puse un espacio de expresión regular reemplazar alrededor de mi resultado y estoy en el negocio: regexp_replace (utl_raw.cast_to_varchar2 (utl_encode.base64_encode (utl_raw.cast_to_raw (t))), '[[: espacio:]] *', '') – Kirby

+2

Hay un pequeño error en su segunda actualización: el parámetro "amount" de dbms_log.substr debe ser "least (substring_length, length (clobInBase64) - (substring_length * n + 1))". La forma en que lo escribió arroja una cantidad negativa, por lo que la subcadena siempre está vacía. – BernardMarx

+1

La codificación BASE64 toma 3 bytes de flujo de entrada y lo convierte en 4 * 3 bytes. Cada uno de estos 3 bytes se asigna a 64 caracteres diferentes (a-z, A-Z, 0-9, "+", "/", por lo que se obtiene el nombre BASE64). Asegúrese de que el valor de substring_length sea un múltiplo entero de 4 para 'BASE64_DECODE', resp. un múltiplo entero de 3 para 'BASE64_ENCODE'. Por lo tanto, su función 'to_base64' puede devolver resultados incorrectos. –

5
+0

puede u por favor dígame cómo hacerlo para el tipo de datos BLOB? Obtengo una excepción "Bugger Too small para CLOB to CHAR o BLOB to RAW conversion (real 50176, maximum 2000)". – Pradeep

+1

Lo siento ... Recibo una excepción "BufferToo pequeño para conversión CLOB a CHAR o BLOB a RAW (50176 reales, máximo 2000)". – Pradeep

+0

oracle utl_raw package acepta solo entrada varchar2, que es corta, así que supongo que todo lo que puedes hacer al respecto es dividir tu blob en fragmentos más pequeños utilizando el paquete dbms_lob, luego convertir cada uno de ellos usando el método que proporcioné y luego volver a unirlos en una sola burbuja. ¿puede dar un código de error que está recibiendo, por cierto? –

0

Todas las publicaciones anteriores son correctas. Hay más de una manera de despellejar a un gato. Aquí hay otra manera de hacer la misma cosa: (basta con sustituir "what_ever_you_want_to_convert" con su cadena y ejecutarlo en 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

hacer url_raw.cast_to_raw() apoyo en el oráculo 6

1

solución con utl_encode.base64_encode y utl_encode.base64_decode tienen una limitación, funcionan solo con cadenas de hasta 32.767 caracteres/bytes.

En caso de que tenga que convertir cadenas más grandes se encontrará con varias o bstáculos.

  • Para BASE64_ENCODE la función tiene que leer 3 Bytes y transformarlos. En el caso de caracteres Multi-Byte (por ejemplo, öäüè€ almacenado en UTF-8, también conocido como AL32UTF8) 3 Carácter no son necesariamente también 3 Bytes. Para leer siempre 3 Bytes primero debe convertir su CLOB en BLOB.
  • El mismo problema se aplica al BASE64_DECODE. La función tiene que leer 4 bytes y transformarlos en 3 bytes. Esos 3 Bytes no son necesariamente también 3 Caracteres
  • Normalmente, un BASE64-String tiene NEW_LINE (CR y/o LF) carácter cada 64 caracteres. Dichos caracteres de nueva línea deben ignorarse durante la decodificación.

Tomando todo esto en consideración la completa solución podría ser ésta:

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; 
Cuestiones relacionadas