XML 是文本型的數據交換結構,對於字符類型的文本交換非常的方便,實際工作中我們往往需要通過 XML 將二進制格式的圖形圖像信息數據進行數據交換。本文從介紹 BASE64 編碼的原理入手,通過采用 C 語言編寫 DB2 的嵌入存儲過程,實現了在數據庫內存中將文本格式的圖片文件到二進制 BLOB 字段之間的轉換,並且就性能優化等提出若幹建議,該設計思路和程序可以廣泛的應用到圖像圖形數據在 XML 的存儲和轉換。
--------------------------------------------------------------------------------
回頁首
XML 存儲圖形圖像的基本原理
XML 作為壹種非常廣泛的數據交換的載體被廣泛的應用到了各行各業的數據交換中。對於圖形圖像數據的轉換,需要采用 Base64 編碼將二進制格式的圖形圖像信息轉換成文本格式再進行傳輸。
Base64 編碼轉換的思想是通過 64 個 ASCII 字符碼對二進制數據進行重新編碼組合,即將需要轉換的數據每三個字節(24 位)為壹組,再將這 24 位數據按每組 6 位進行重新劃分,在每組的最高 2 位填充 0 最終成壹個完整的 8 位字節。如果所要編碼的數據的字節數不是 3 的整數倍,需要在最後壹組數據填充 1 到 2 個字節的 0 字節。例如:我們對 ABC 進行 BASE64 的編碼,ABC 的編碼值:A(65), B(66), C(67)。再取二進制 A(01000001)B(01000010)C(01000011)連接起來構成 010000010100001001000011,然後按 6 位為單位分成 4 個數據塊並在最高位填充兩個 0 後形成 4 個字節的編碼後的值(00010000)(00010100)(00001001)(00000011)。再將 4 個字節的數據轉換成十進制數為(16)(20)(19)(3)。最後根據 BASE64 給出的 64 個基本字符表,查出對應的 ASCII 碼字符(Q)(U)(J)(D)。這裏的值實際就是數據在字符表中的索引。
BASE64 字符表:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789。
某項目的數據交換采用 XML 的為介質,XML 的結構包括個人基本信息:姓名、性別、相片等信息,其中相片信息是采用經過 BASE64 函數轉換後的文本型數據,圖像圖形信息通過 BASE64 進行數據轉換後,形成文本格式的數據類型,再將相應的數據存放到 XML 中,最終形成可供交換的文本型的 XML 數據結構。
XML 的數據結構如下所示:
<?xml version=”1.0” encoding=”UTF-8” ?>
<HeadInfo>
<TotalNum>10<TotalNum>
<TransDate>2007-10-18</TransDate>
</HeadInfo>
<Data>
<Name> 張三 </Name>
<Sex> 男 </Sex>
<Photo>/9j/4AAQSkZJRgABAQAAAQABAAD......</Photo>
<Data>
--------------------------------------------------------------------------------
回頁首
相片數據在 DB2 嵌入式 C 程序的實現方法
該項目要求能夠在 DB2 數據庫中將相片數據存儲為二進制 BLOB 格式。我們采用 DATASTAGE 進行 XML 數據加載,將 XML 中的姓名、性別等基本數據項加載到相應的字段,其中文本型的相片數據則加載到 CLOB 字段中,再按照 BASE64 的編碼規則進行逆向轉碼,整個數據流程如下圖所示:
圖 1. 相片存儲流程圖
用戶的相片每天的更新數據為 30 萬條,而且每個相片的平均大於 32KB,為了獲得最佳的數據庫性能,選擇采用 C 存儲過程的方式開發了 BASE64 的轉換函數。每次函數讀取存儲在 CLOB 字段的文本格式數據全部存儲到內存中,並且通過 decode 函數在內存中進行轉碼,轉碼後再存入數據庫中。
程序的清單 1 是逐行讀取 CLOB 字段,並且調用 decode 函數進行轉碼;程序的清單 2 是 decode 函數的關鍵性代碼。完整的程序見源代碼下載部分。
清單 1. 讀入 CLOB,寫入 BLOB 字段
EXEC SQL BEGIN DECLARE SECTION;
SQL TYPE IS CLOB(100 K) clobResume; //CLOB 結構體變量
SQL TYPE IS BLOB(100 K) blobResume; //BLOB 結構體變量
sqlint16 bobind;
sqlint16 lobind;
sqlint16 cobind;
sqlint32 idValue;
EXEC SQL END DECLARE SECTION;
int clob2bin(void)
{
// 聲明 SQLCA 結構
struct sqlca sqlca;
int charNb;
int lineNb;
long n;
n=0;
// 定義數據庫遊標
EXEC SQL DECLARE c1 CURSOR WITH HOLD FOR
SELECT czrkxp_a
FROM CZRK_blob for update;
EXEC SQL OPEN c1;
// 活動 CLOB 字段的信息,已經 CLOB 字段的大小
EXEC SQL FETCH c1 INTO :clobResume:cobind;
// 循環讀取 CLOB 字段,並且調用 DECODE 轉碼函數
while (sqlca.sqlcode != 100)
{
if (cobind < 0)
{
printf(“ NULL LOB indicated.\n”);
}
else
{
n++;
decode(); // 文本格式到二進制流的轉碼函數
printf(“\nCurrent Row =%ld”,n);
// 數據寫入 BLOB 字段
EXEC SQL update czrk_blob set czrkxp_blob = :blobResume
where current of c1; ;
// 提交事務
EXEC SQL COMMIT;
}
EXEC SQL FETCH c1 INTO :clobResume:cobind ;
}
// 關閉遊標
EXEC SQL CLOSE c1;
EXEC SQL COMMIT;
return 0;
}
清單 2. 文本文件到二進制文件的轉換
void decode( void )
{
unsigned char in[4], out[3], v;
int I, len;
long j,k;
j = -1;
k=0;
// 將讀入 CLOB 結構體變量的數據進行轉換
while( j < clobResume.length){
for( len = 0, I = 0; I < 4 && ( j < clobResume.length ); i++ ) {
v = 0;
while((j < clobResume.length) && v == 0 ) {
j++;
v = (unsigned char) clobResume.data[j];
v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v – 43 ]);
if( v ) {
v = (unsigned char) ((v == ‘$’) ? 0 : v – 61);
}
}
if( j < clobResume.length ) {
len++;
if( v ) {
in[ I ] = (unsigned char) (v – 1);
}
}
else {
in[i] = 0;
}
}
if( len ) {
decodeblock( in, out );
// 寫入到 BLOB 結構體變量中
for( I = 0; I < len – 1; i++ ) {
blobResume.data[k] = out[i];
k++;
}
}
}
blobResume.length= k;
}
--------------------------------------------------------------------------------
回頁首
數據的轉換效率和優化建議
在 IBM P570 數據庫服務器上運行,該程序的運行效率非常高,先後進行了幾個數量級的測試,最終平均測試的轉換效率為:每 1 萬筆數據記錄,轉換的效率 55 秒,即 182 條 / 秒。值得註意的是,整個轉換過程占用 CPU 的量並不特別大,主要的性能瓶頸在磁盤陣列中。
以後可以進壹步在以下方面進行調優,確保程序轉換的效率更高:
1)采用多進程調用的方式,以獲得更高的並發數量;
2)采用每 10 次或者 100 次提交事務的方式,減少訪問磁盤的次數;
3)將 CLOB 和 BLOB 分別放置在不同的表空間上,並且將表空間分布在在多個磁盤上,獲得最佳的磁盤訪問速度。