一、问题描述
由于笔者所在实验室经常要进行激光雷达实验,实验会产生很多的数据文件,通常会对数据文件进行处理后连带实验结果打包,一般打包后的文件大小在5M到20M左右。由于实验产生的文件非常多,不便于管理,故需要开发一个对文件进行管理的数据库管理系统。在权衡了是将文件入库还是将文件的路径入库进行管理的利弊之后,笔者决定实现文件入库,因为这样能保证数据库的安全性及易操作性。而且由于文件较小,读写文件的时间开销在几秒钟内是可以接受的。路径入库虽然节省了读写文件的时间开销,可是数据库却很脆弱,因为路径是很容易被修改或者删除的。笔者在做了详细的需求分析和系统设计之后,决定用PowerBuiler 9.0+ASA8.0来开发这个系统。
二、程序实现
1.数据库创建
用Sybase Central工具创建一个数据库 LidarData.db,并添加表,表的设计如图1示。

图1实验数据文件表的设计
realfile字段在ASA8.0中定义为long binary类型,用来存储BLOB型数据,不同数据库其类型不尽相同,如SQL Server 2000中则可用image、text或ntext类型,Oracle 9i则可用Long Raw、BLOB、Clob或nclob类型。
创建完成后,用ODBC接口连接数据库。
2.主界面与主要功能模块的实现
采用PB提供的各种控件,设计系统的主界面如图2示。

图2 系统的主界面
(1)两个重要函数的创建与实现
在对各功能模块进行代码编写之前,首先要创建两个自定义函数,用于对文件的读操作和写操作,这两个函数分别是f_readfile()和f_writefile()。实现代码如下:
//f_readfile()函数声明与实现
Function Name: f_readfile
Access: public
Return Type: BLOB
Pass By: Value
Argument Type: string
Argument Name:strfilename //文件路径传递参数
//该函数实现把二进制文件读入一个BLOB类型变量的功能
//函数体:
int intcount_readfile // 用于记录文件分块数
int intfileno // 用于存放文件句柄
int inttemp // 用于循环记数
long lngfilelength // 用于存放文件长度
BLOB blobret,blobtemp // 用于完整和临时分块装载文件数据
lngfilelength=filelength(strfilename) // 得到文件长度
intcount_readfile=Ceiling(lngfilelength/32765) // 得到文件块数
intfileno=fileopen(strfilename,streammode!,Read!,Shared!) // 文件句柄
for inttemp=1 to intcount_readfile //将文件转换为 BLOB 型数据
fileread(intfileno,blobtemp) // 在 PowerBuilder中文件读写函数可以自动移动指针
blobret=blobret+blobtemp
next
fileclose(intfileno) // 释放文件资源
return blobret //返回BLOB类型的数据
//f_writefile()函数声明与实现
Function Name: f_writefile
Access: public
Return Type: boolean
Pass By: Value
Argument Type1: string
Argument Type2: BLOB
Argument Name1: strfilename //文件路径传递
Argument Name2: blobvariable //用于写入文件的BLOB类型数据
//该函数实现把BLOB类型变量的值写入文件的功能
//函数体
int intcount_writefile// 用于记录文件分块数
int inttemp // 用于循环记数
int intfileno
BLOB blobtemp // 用于临时分块装载 BlOB 数据
intcount_writefile=ceiling(len(blobvariable)/32765) // 得到 BLOB 数据的长度
//参见附录的len()函数说明
intfileno=fileopen(strfilename,streammode!,write!,LockWrite!,replace!) // 得到文件句柄
for inttemp=1 to intcount_writefile // 将 BLOB 型数据转换为文件
blobtemp=blobmid(blobvariable,32765*(inttemp - 1)+1,32765)//参见附录的blobmid()函数说明
filewrite(intfileno,blobtemp) //将blobtemp中的数据写入文件
next
fileclose(intfileno) // 释放文件资源
return true
(2)数据文件导入功能的实现
该模块主要实现实验数据文件以及相关附加属性的入库,界面如图3示:

图3数据文件导入功能界面
对“确认导入”按钮的Click事件编写代码:
string fileno,filename,datatype,lidarname,getaddress,weather,addition,gettime
BLOB real_file //声明各字段对应的变量名称
int i
fileno=trim(sle_file_no.text)
filename=name
datatype=ddlb_datatype.text
lidarname=trim(ddlb_lidar_name.text)
gettime=trim(sle_get_time.text)
getaddress=trim(ddlb_get_address.text)
weather=trim(ddlb_weather.text)
addition=trim(mle_addition.text) //得到界面所有控件的输入值
real_file=f_readfile(docname) //读取指定文件到real_file,用到了f_readfile函数
connect;
insert into lidardata_tab values(:fileno,:filename,:datatype,:lidarname,:gettime,:getaddress,:weather,:addition,:real_file);
//将新增的记录插入到数据库的表中
disconnect;
connect;
updateblob lidardata_tab set realfile=:real_file where file_no=:fileno;
//将real_file插入到记录的BLOB字段中(该步骤不能少,Insert操作并未实现BLOB类型
//的插入)updateblob是对BLOB类型数据的专用修改语句
string nfilename
select file_name into :nfilename from lidardata_tab where file_no=:fileno;
if nfilename<>"" then
messagebox("","导入成功!")
else
messagebox("","导入失败!")
end if
//检验记录导入是否成功完成
w_test.dw_1.settransobject(sqlca)
w_test.dw_1.retrieve()
(3)文件导出功能的实现
该功能实现了将选择的记录包含的文件导出到指定路径的功能,并可以打开导出的文件,界面如图4所示。

图4 数据文件导出功能界面
对“确认导入”按钮的Click事件编写代码(路径信息赋给一个全局变量)如下:
BLOB outfile
string outfilename,str_filename,str_null
boolean flag,lb_exist
int flag1,flag2
selectblob realfile into :outfile from lidardata_tab where file_no=:selectfile;
//selectfile为一全局变量,存储选定记录的编号
//outfile被赋给选定记录中realfile字段的内容
select file_name into :outfilename from lidardata_tab where file_no=:selectfile;
//得到选定记录中文件名的内容
if FileExists(path+"\"+outfilename) then
flag2=messagebox("提醒","该路径下已经有一个同名的文件,是否继续导出覆盖该文件?",Question!,YesNO!)
end if
if flag2=2 then
return
end if
flag=f_writefile(path+"\"+outfilename,outfile)
if flag=true then
messagebox("OK","导出完成!")
flag1=messagebox("???","是否运行已导出的数据文件?",Question!,YesNoCancel! )
end if
str_filename=trim(sle_1.text)+"\"+outfilename
lb_exist=FileExists(str_filename)
if(flag1<>1) then
return
end if
if trim(sle_1.text)="" then
messagebox("提示","请选择要打开的数据文件!")
return
end if
if lb_exist=false then
messagebox("提示","要打开的数据文件错误或不存在")
return
end if
ShellExecute(Handle(Parent),str_null,str_filename,str_null,str_null,1)
//调用WinAPI函数运行导出的文件(文件必须要有系统能识别的格式)
三、结语
通过对以上关于BLOB数据类型的使用与函数操作,已经可以把BLOB当作一个普通的数据类型进行操作和管理,不同的是需要编写两个特别的函数f_readfile()和f_writefile()。但是不能忽视的是:这种与数据库相关的应用只适合文件较小的时候,因为太大的文件会引起的巨大时间开销。此外,还要特别注意Selectblob和Updateblob语句的用法。
|