Oracle BLOB в base64 CLOB

могу ли я преобразовать Oracle BLOB в Base64 CLOB за один раз?

как:

CREATE TABLE test
(
image BLOB,
imageBase64 CLOB
);

INSERT INTO test(image)
VALUES (LOAD_FILE('/full/path/to/new/image.jpg'));

UPDATE test SET imageBase64 = UTL_ENCODE.base64_encode(image);

commit;

Я знаю, что могу добавить функции / сохраненный proc для выполнения работы. Аспект производительности очень важен, поэтому я спрашиваю, есть ли способ преодолеть ограничение 32K, напрямую вставляя данные в CLOB.

4 ответов


при условии, что сохраненные процессы будут, несмотря на жизнеспособную альтернативу для вас, вот одно возможное решение вашей проблемы ...

во-первых, давайте сделаем это красиво


эта функция есть отсюда должен делать свою работу.

CREATE OR REPLACE FUNCTION base64encode(p_blob IN BLOB)
  RETURN CLOB
-- -----------------------------------------------------------------------------------
-- File Name    : http://oracle-base.com/dba/miscellaneous/base64encode.sql
-- Author       : Tim Hall
-- Description  : Encodes a BLOB into a Base64 CLOB.
-- Last Modified: 09/11/2011
-- -----------------------------------------------------------------------------------
IS
  l_clob CLOB;
  l_step PLS_INTEGER := 12000; -- make sure you set a multiple of 3 not higher than 24573
BEGIN
  FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(p_blob) - 1 )/l_step) LOOP
    l_clob := l_clob || UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(p_blob, l_step, i * l_step + 1)));
  END LOOP;
  RETURN l_clob;
END;
/

тогда обновление может выглядеть как

UPDATE test SET imageBase64 = base64encode(image);

обратите внимание, что, возможно, функция должна быть оптимизирована с помощью функции DBMS_LOB.Добавьте вместо этого оператор конкатенации. Попробуйте это, если у вас есть проблемы с производительностью.


Я решил эту же проблему на работе, используя хранимую процедуру Java. В таком подходе нет чанкинга/контатенации VARCHAR2s, поскольку возможность кодирования / декодирования base64 изначально встроена в Java, просто написание функции Oracle, которая тонко обертывает метод Java, работает хорошо и является высокопроизводительной, так как как только вы выполнили его несколько раз, HotSpot JVM компилирует Java proc в низкоуровневый код (высокая производительность так же, как и хранимая функция C). Я буду редактировать этот ответ позже и добавьте сведения об этом коде Java.

но чтобы отступить всего на один шаг, вопрос, почему вы храните эти данные как BLOB и base64 закодированы (CLOB)? это потому что у вас есть клиенты, которые хотят использовать данные в последнем формате? Я бы предпочел хранить только формат BLOB. Одна из причин заключается в том, что кодированная версия base64 может быть вдвое больше размера исходного двоичного BLOB, поэтому сохранение их обоих означает, возможно, 3x место хранения.

одно решение, которое я реализовал на работе, чтобы создать свою собственную сохраненную функцию Java base64_encode() который кодирует binary --> base64, а затем использует эту функцию для кодирования base64 на лету во время запроса (это не дорого). Со стороны приложения / клиента вы запросите что-то вроде SELECT base64_encode(image) FROM test WHERE ...

если код приложения нельзя коснуться (т. е. приложение COTS) или если ваши разработчики не в восторге от использования функции, вы можете абстрагировать это для них (так как вы используете 11g+), используя виртуальный (вычисляемый) столбец в таблице, который содержит вычисляемый base64_encode(image). Он будет функционировать как представление, поскольку физически не будет хранить закодированные Клобы, а будет генерировать их во время запроса. Для любого клиента они не смогут сказать, что не читают физическую колонку. Другое преимущество заключается в том, что если вы когда-либо обновляете jpg (BLOB), виртуальный CLOB немедленно и автоматически обновляется. Если вам когда-либо нужно вставить/обновить / удалить огромный пакет из BLOBs вы сэкономите 66% объема redo / archivelog от необходимости обрабатывать все CLOBs.

наконец, для производительности убедитесь, что вы используете Securefile LOBs (как для BLOBs, так и для CLOBs). Они действительно намного быстрее и лучше почти во всех отношениях.

обновление - я нашел свой код, по крайней мере, версию, которая использует хранимую процедуру Java, чтобы сделать обратное (преобразование кодированного CLOB base64 в его двоичную версию BLOB). Оно не будет трудно написать обратное.

--DROP FUNCTION base64_decode ;
--DROP java source base64;

-- This is a PLSQL java wrapper function
create or replace
FUNCTION base64_decode (
   myclob clob)
   RETURN blob
AS LANGUAGE JAVA
   NAME 'Base64.decode (
            oracle.sql.CLOB) 
            return oracle.sql.BLOB';
/

-- The Java code that base64 decodes a clob and returns a blob.
create or replace and compile java source named base64 as
import java.sql.*;
import java.io.*;
import oracle.sql.*;
import sun.misc.BASE64Decoder;
import oracle.jdbc.driver.*;

public class Base64 {

public static oracle.sql.BLOB decode(oracle.sql.CLOB  myBase64EncodedClob)
  {

    BASE64Decoder base64   = new BASE64Decoder();
    OutputStream  outstrm  = null;
    oracle.sql.BLOB myBlob = null;
    ByteArrayInputStream instrm = null;

    try
    {
      if (!myBase64EncodedClob.equals("Null"))
      {
        Connection conn = new OracleDriver().defaultConnection();
        myBlob = oracle.sql.BLOB.createTemporary(conn, false,oracle.sql.BLOB.DURATION_CALL);
        outstrm = myBlob.getBinaryOutputStream();
        ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();

        InputStream in = myBase64EncodedClob.getAsciiStream();
        int c;
        while ((c = in.read()) != -1)
        {
          byteOutStream.write((char) c);
        }

        instrm = new ByteArrayInputStream(byteOutStream.toByteArray());

        try  // Input stream to output Stream
        {
          base64.decodeBuffer(instrm, outstrm);
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }

        outstrm.close();
        instrm.close();
        byteOutStream.close();
        in.close();
        conn.close();
      }

    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    return myBlob;

  }  // Public decode

} // Class Base64
;
/

самый простой способ, который я нашел, который работает со специальными символами (в вашем случае у вас нет этой проблемы), использует dbms_lob.конвертоклоб.

создать инкапсулированные процедуры:

CREATE OR REPLACE FUNCTION blob2clob(blob_i IN BLOB) RETURN CLOB IS
  l_clob         CLOB;
  l_dest_offset  NUMBER := 1;
  l_src_offset   NUMBER := 1;
  l_amount       INTEGER := dbms_lob.lobmaxsize;
  l_clob_csid    NUMBER := nls_charset_id('WE8ISO8859P15'); --dbms_lob.default_csid;
  l_lang_context INTEGER := dbms_lob.default_lang_ctx;
  l_warning      INTEGER;
BEGIN
  ---------------------------
  -- Create Temporary BLOB --
  ---------------------------
  dbms_lob.createtemporary(lob_loc => l_clob,
                           cache   => TRUE);
  --------------------------
  -- Convert CLOB to BLOB --
  --------------------------
  dbms_lob.converttoclob(dest_lob     => l_clob,
                         src_blob     => blob_i,
                         amount       => l_amount,
                         dest_offset  => l_dest_offset,
                         src_offset   => l_src_offset,
                         blob_csid    => l_clob_csid,
                         lang_context => l_lang_context,
                         warning      => l_warning);
  --
  RETURN l_clob;
END blob2clob;

затем вы можете использовать:

blob2clob(utl_encode.base64_encode(image))