我可以一次性将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;
Run Code Online (Sandbox Code Playgroud)
我知道我可以添加函数/存储过程来完成工作.性能方面非常重要,所以我问是否有办法通过直接将数据推送到CLOB来克服32K限制.
这个功能从这里得到应该做的工作.
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;
/
Run Code Online (Sandbox Code Playgroud)
然后更新可能看起来像
UPDATE test SET imageBase64 = base64encode(image);
Run Code Online (Sandbox Code Playgroud)
请注意,可能应使用函数DBMS_LOB.APPEND而不是该连接运算符来优化函数.如果您遇到性能问题,请尝试使用.
我通过使用Java存储过程在工作中解决了同样的问题.这种方法中没有涉及VARCHAR2的分块/连接,因为编码/解码base64的能力本身构建在Java中,只需编写一个简单包装Java方法的Oracle函数就可以正常运行并且是高性能的,因为只要你已经执行了几次,HotSpot JVM将Java proc编译成低级代码(高性能就像C存储函数一样).我稍后会编辑这个答案,并添加有关该Java代码的详细信息.
但是要退一步,请问为什么要将这些数据存储为BLOB和base64编码(CLOB)? 是因为您有客户想要以后一种格式使用数据吗?我真的更喜欢只存储BLOB格式.其中一个原因是base64编码版本的大小可能是原始二进制BLOB的两倍,因此存储它们可能意味着存储量的3倍.
一个解决方案,我在工作中实现的,创建自己的Java存储函数base64_encode(),编码二进制 - > base64,然后使用该函数在查询时动态编码base64(它并不昂贵).从应用程序/客户端,您可以查询类似的内容SELECT base64_encode(image) FROM test WHERE ...
如果无法触及应用程序代码(即COTS应用程序)或者您的开发人员对使用函数不感兴趣,您可以通过使用VIRTUAL(计算)列来为它们抽象(因为您使用的是11g +)包含计算的表base64_encode(image).它的功能类似于视图,因为它不会物理存储编码的CLOB,但会在查询时生成它们.对于任何客户,他们都无法告诉他们他们没有阅读物理专栏.另一个好处是,如果您更新了jpg(BLOB),则会立即自动更新虚拟CLOB.如果您必须插入/更新/删除大量BLOB,则可以节省66%的重做/存档日志,而无需处理所有CLOB.
最后,为了提高性能,请确保使用SecureFile LOB(包括BLOB和CLOB).几乎在所有方面,它们确实更快更好.
更新 - 我找到了我的代码,至少是使用Java存储过程执行相反操作的版本(将base64编码的CLOB转换为其二进制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
;
/
Run Code Online (Sandbox Code Playgroud)
如果存储过程尽管对您来说是可行的替代方案,这里有一个可能的解决方案来解决您的问题......
首先,让我们把base64encode()蒂姆·霍尔的好功能变成一个程序......
create or replace procedure base64encode
( i_blob in blob
, io_clob in out nocopy clob )
is
l_step pls_integer := 22500; -- make sure you set a multiple of 3 not higher than 24573
l_converted varchar2(32767);
l_buffer_size_approx pls_integer := 1048576;
l_buffer clob;
begin
dbms_lob.createtemporary(l_buffer, true, dbms_lob.call);
for i in 0 .. trunc((dbms_lob.getlength(i_blob) - 1 )/l_step) loop
l_converted := utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(i_blob, l_step, i * l_step + 1)));
dbms_lob.writeappend(l_buffer, length(l_converted), l_converted);
if dbms_lob.getlength(l_buffer) >= l_buffer_size_approx then
dbms_lob.append(io_clob, l_buffer);
dbms_lob.trim(l_buffer, 0);
end if;
end loop;
dbms_lob.append(io_clob, l_buffer);
dbms_lob.freetemporary(l_buffer);
end;
Run Code Online (Sandbox Code Playgroud)
这里的"技巧"是在调用过程/函数时直接使用持久性LOB定位器.为什么"持久"?因为如果您创建一个返回LOB的函数,那么在后台创建了一个临时LOB,这意味着需要一些TEMP磁盘/内存使用和LOB内容复制.对于大型LOB,这可能意味着性能受到打击.为了满足您使其成为最佳性能的要求,您应该避免使用此TEMP空间.因此,对于这种方法,必须使用存储过程而不是函数.
然后,当然,必须使用持久性LOB定位器来提供该过程.您必须再次使用存储过程,例如,首先在表中插入一个空的LOB(有效地创建一个新的LOB定位符),然后将新创建的LOB定位符提供给base64编码例程......
create or replace procedure load_and_encode_image
( i_file_name in varchar2 )
is
l_input_bfile bfile := bfilename('DIR_ANYTHING', i_file_name);
l_image_base64_lob test.imageBase64%type;
l_image_raw test.image%type;
begin
insert into test(image, imageBase64)
values (empty_blob(), empty_clob())
returning image, imageBase64
into l_image_raw, l_image_base64_lob;
begin
dbms_lob.fileopen(l_input_bfile);
dbms_lob.loadfromfile(
dest_lob => l_image_raw,
src_lob => l_input_bfile,
amount => dbms_lob.getlength(l_input_bfile)
);
dbms_lob.fileclose(l_input_bfile);
exception
when others then
if dbms_lob.fileisopen(l_input_bfile) = 1 then
dbms_lob.fileclose(l_input_bfile);
end if;
raise;
end;
base64encode(
i_blob => l_image_raw,
io_clob => l_image_base64_lob
);
end;
Run Code Online (Sandbox Code Playgroud)
注意:当然,如果你只对64个小文件进行编码(实际大小取决于你的PGA设置,我猜 ;这是DBA的问题,这是),那么基于函数的方法可能与此程序同样有效 -基于一个.在我的笔记本电脑上编码200MB文件的Base64使用功能+更新方法花了55秒,使用程序方法花费了14秒.不完全是速度恶魔,所以选择适合你的需求.
注意:我相信这个基于过程的方法可以通过将文件读取到循环中的内存块,base64-将块编码到另一个内存块并将它们都附加到目标持久LOB来进一步加速.这样,您应该避免test.image通过该base64encode()过程重新读取完整的LOB内容,从而使工作负载更加轻松.