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))