Question

I am using the following function to convert a large base64 encoded file(image or voice) into a blob file and store it in the Oracle database (Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - Production).

I am able to store it and retrieve it but the image is getting corrupted. Only a potion of the image is getting retrieved. I tried using small images(11KB size) and it is working fine. But for larger images(88KB to 700KB) only a portion of the image is retrieved.

The problem is with the base-64 decoding. Earlier I was not able to get even the smaller image due to corruption, but when I increased the buffer size, it came fine. Now the buffer size is at its maximum at 32767 as its the maximum for varchar2 and raw.

Can anyone provide a suitable workaround or solution.

function decode_base64(p_clob_in in clob) return blob is
    v_blob blob;
    v_result blob;
    v_offset integer;
    v_buffer_size binary_integer := 32767;     -- 24, 48, 3072
    v_buffer_varchar varchar2(32767);
    v_buffer_raw raw(32767);

  begin

    if p_clob_in is null then
      return null;
    end if;

    dbms_lob.createtemporary(v_blob, true);
    v_offset := 1;

    for i in 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size) 
    loop
      dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
      v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
      v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
      dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
      v_offset := v_offset + v_buffer_size;
    end loop;

    v_result := v_blob;
    dbms_lob.freetemporary(v_blob);

    return v_result;

  end decode_base64;

The code that i use to call the function and insert the blob into the table is given below...

 PROCEDURE create_notes (
      p_task_id              IN       NUMBER
     ,p_note_title           IN       VARCHAR2
     ,p_note_detail          IN       VARCHAR2
     ,p_attach_name          IN       VARCHAR2
     ,p_attachment           IN       CLOB
     ,p_attach_type          IN       VARCHAR2
     ,x_return_code          OUT      VARCHAR2
     ,x_return_message       OUT      VARCHAR2
   )
IS
  l_blob_data BLOB;
BEGIN
.
.
.
 IF p_attachment IS NOT NULL THEN


            SELECT incident_id INTO l_pk1_value FROM csf_ct_tasks where task_id = p_task_id;

                        l_blob_data :=  xx_utl_base64.decode_base64(p_attachment);
INSERT INTO fnd_lobs
                        (file_id, file_name, file_content_type, upload_date,
                        expiration_date, program_name, program_tag, file_data,
                        LANGUAGE, oracle_charset, file_format
                        )
                        VALUES (l_media_id, p_attach_name,p_attach_type,  -- 'audio/mpeg','application/pdf','image/jpeg'
                        SYSDATE,
                        NULL, 'FNDATTCH', NULL, l_blob_data,               --l_blob_data,EMPTY_BLOB ()
                        'US', 'UTF8', 'binary'
                        )
                        RETURNING file_data
                        INTO x_blob;
COMMIT;
END IF:

Attaching the original picture and its decoded version, below.Original picture

Decoded image

Was it helpful?

Solution

I got the below code from net. It worked like a charm. Dont know whats the problem with my old code though.

 FUNCTION base64decode(p_clob CLOB)
         RETURN BLOB

        IS
         l_blob    BLOB;
         l_raw     RAW(32767);
         l_amt     NUMBER := 7700;
         l_offset  NUMBER := 1;
         l_temp    VARCHAR2(32767);
        BEGIN
         BEGIN
           DBMS_LOB.createtemporary (l_blob, FALSE, DBMS_LOB.CALL);
           LOOP
             DBMS_LOB.read(p_clob, l_amt, l_offset, l_temp);
             l_offset := l_offset + l_amt;
             l_raw    := UTL_ENCODE.base64_decode(UTL_RAW.cast_to_raw(l_temp));
             DBMS_LOB.append (l_blob, TO_BLOB(l_raw));
           END LOOP;
         EXCEPTION
           WHEN NO_DATA_FOUND THEN
             NULL;
         END;
         RETURN l_blob;
        END;

OTHER TIPS

I tried your function with a v_buffer_size of 8192 and it worked fine. I've tried several numbers smaller than 32767 and they all worked fine, so try something less than that.

For those who's still looking for a correct solution - you need to decode input data in multiples of 4. In case input contains non-base64 symbols (which are ignored by built-in function utl_encode.base64_decode), it might lead to incorrect results on large files.

I've found a lot of samples on the web which do not correctly decode, posting my code below

    FUNCTION base64_decode(p_content CLOB) RETURN BLOB
    IS
        C_CHUNK_SIZE CONSTANT INTEGER := 12000; -- should be a multiple of 4
        C_NON_BASE64_SYM_PATTERN CONSTANT VARCHAR2(20) := '[^A-Za-z0-9+/]';
        l_chunk_buf VARCHAR2(12000);
        l_chunk_b64_buf RAW(9000);
        l_chunk_offset INTEGER := 1;
        l_chunk_size INTEGER;
        l_res BLOB;

        FUNCTION get_next_full_base64_chunk(l_data CLOB, p_cur_pos IN OUT INTEGER, p_desired_size INTEGER, p_cur_size IN OUT INTEGER) RETURN VARCHAR2 IS
            l_res VARCHAR2(12000);
            l_tail_desired_size INTEGER;
        BEGIN
            l_res := dbms_lob.substr(l_data, p_desired_size, p_cur_pos);
            p_cur_pos := p_cur_pos + p_desired_size;
            IF l_res IS NULL THEN
                RETURN NULL;
            END IF;

            l_res := regexp_replace(l_res, C_NON_BASE64_SYM_PATTERN, '');
            p_cur_size := p_cur_size + length(l_res);

            l_tail_desired_size := 4 - mod(p_cur_size, 4);
            IF l_tail_desired_size = 4 THEN
                RETURN l_res;
            ELSE
                RETURN l_res || get_next_full_base64_chunk(l_data, p_cur_pos, l_tail_desired_size, p_cur_size);
            END IF;
        END;

    BEGIN
        dbms_lob.createtemporary(l_res, false);

        WHILE true
        LOOP
            l_chunk_size := 0;
            l_chunk_buf := get_next_full_base64_chunk(p_content, l_chunk_offset, C_CHUNK_SIZE, l_chunk_size);
            EXIT WHEN l_chunk_buf IS NULL;
            l_chunk_b64_buf := utl_encode.base64_decode(utl_raw.cast_to_raw(l_chunk_buf));
            dbms_lob.writeappend(l_res, utl_raw.length(l_chunk_b64_buf), l_chunk_b64_buf);
        END LOOP;

        RETURN l_res;
    END;

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top