Thursday, May 24, 2007

图像数据库 [整理]

图像数据库 [整理]


1. 用ORACLE数据库存储海量图像数据


==============================
2004-06-29 10:32:01
郑浩 张蔚

在利用ORACLE数据库平台进行数据库系统的开发过程中,对于海量图像数据的管理,目前,大都采用表+实体的方法,即图像数据以文件形式存放于指定的计算机目录下,在数据库表中只反映图像数据文件的存储路径。这种管理模式,给数据的维护增加了难度,同时,也给数据的安全带来一定的隐患。因此,要真正做到各类数据在数据库中安全管理,研究和探索直接将海量图像数据存储在数据库关系表中的方法是非常必要的。
笔者在Visual Basic 6.0开发环境中,采用客户机/服务器的工作方式,针对ORACLE数据库关系表中存储大量图像数据的问题和存储海量图像数据的策略与方法进行了初步探讨,提出了一套基本解决方案,供读者参考。

一、大对象数据类型介绍

在关系型数据库中,大数据量图像数据等大型对象是由lob型字段来进行存取的。在Oracle8i中,正式引入了此标准,以适应多媒体大对象

处理的需求。Oracle数据库中,lob型数据有以下几种:

Lob类型 说明
Clob: 和Oracle7的long型相似,clob可以存储单字节型数据
Nclob: Nclob存储定宽的多字节国家字符集数据
Blob: 和Oracle7中的long raw类型相似。可以存储无结构的二进制数据。Oracle8没有对这种数据进行解释,只是按照原来的形式存储和检索它。
Bfile: Bfile允许对Oracle数据库以外存储的大型二进制文件进行只读形式的访问。和其它三种lob类型数据不同的是,bfile类型数据存储在一个单独的文件中,该文件不由Oracle来维护。

特点:
1.在Oracle7中,相应的long或long raw字段有2g的限制,而lob的限制是4g 。
2.lob可以使用调用接口OCI或者由pl/sql利用dbms_lob包进行操纵。
3.lob不象long型那样每个表中最多只有一个字段的限制,其可以有多个,而又可以利用触发器的特性。
4.lob数据处理可以获得与其它数据同样的事物特性。
5.lob的存储比较特殊,它并不是跟其他数据存储在同一个数据库表中,而是可以单独存放于不同的表空间中,由一个定位符指向实际的lob数据。

二、存储海量图像数据的策略

图像数据库技术一直致力于解决海量数字图像的有效存储和管理问题。它是数据库技术的继承和发展,一方面,图像数据和文本数据存在

着本质的区别,在文本数据领域得以成功应用的传统数据库技术,如果一成不变的照搬到图像数据库领域,结果往往是低效,甚至无效;另一方面,传统数据库的许多成果,如SQL语言、索引技术等都值得图像数据库借鉴。上述两个方面的结合成为目前图像数据库技术发展的主流。
BLOB大对象数据是数据量很大的数据类型,它会占用大量的硬盘空间、内存和网络资源,因此合理地设计包含有BLOB大对象数据类型的属性表,对提高存储效率、查询速度有很大的影响。一般BLOB大对象的设计原则如下:

(1) 尽量不使用BLOB大对象
二进制大对象并不一定要存储为text、ntext或者image数据类型,它们也可以作为varchar或者varbinary数据类型存储在表格中。数据类型的选择要根据将要存储的BLOB的实际大小。如果数据不会超过8K,那么就使用Varchar或者varbinary数据类型。如果这些大对象的尺寸超过8K,那么就使用text、ntext或者image数据类型。

(2)何时使用BLOB数据类型

在下列情况下,我们可能要使用到BLOB数据类型
·您要将OLE对象(如图形、声音等)存入您的数据库中;
·您要将大型的二进制对象存入您的数据库中;
·您所要操纵的文本对象过大,以致于一般的字符串函数无法对其操作;
·您所使用数据库的数据类型oracle不能支持,所以您只能使用blob函数对其进行操纵。

(3) 最好将BLOB存储在数据库中

常见的设计问题是将图片存在数据库中还是存在文件系统中。在大多数情况下,最好把图片文件与其它数据一起存在数据库中。因为将影

像数据文件存储在数据库中有许多优点:
⑴易于管理。当BLOB与其他数据一起存储在数据库中时,BLOB和表格是数据一起备份和恢复。这样就降低了表格数据与BLOB数据不同步的机会,而且降低了其他用户无意中删除了文件系统中BLOB数据位置的路径和风险。另外,将数据存储在数据库中BLOB和其他数据的插入、更新和删除都在同一个事务中实现。这样就确保了数据的一致性和文件与数据库之间的一致性。还有一点好处是不需要为文件系统中的文件单独设置安全性。
⑵可伸缩性。尽管文件系统被设计为能够处理大量不同大小的对象,但是文件系统不能对大量小文件进行优化。在这种情况下,数据库系统可以进行优化。
⑶可用性。数据库具有比文件系统更多的可用性。数据库复制允许在分布式环境中复制、分配和潜在的修改数据。在主系统失效的情况下,日志转移提供了保留数据库备用副本的方法。

  

当然,在某些情况下,将图片存储在文件系统中将是更好的选择:
(1)使用图片的应用程序需要数据流性能,例如实时的视频重现。
(2)象Microsoft PhotoDraw或者Adobe Photoshop这样的应用程序经常访问BLOB,这些应用程序只知道怎样访问文件。
(3)需要使用一些NTFS文件系统中的特殊功能,例如远程存储。

三、海量数据存储、备份及分发

(1)存储
目前对数据的存储可分为两大类型,一种是传统的以主机为中心的存储方式,另一种是基于网络的以网络为中心的存储方式。与传统的存储方式相比,网络存储具有更大的灵活性,可以实现对所有数据的共享,可以较好地保持数据的一致性、完整性和安全性。存储方式有许多,最常用的是磁带库和磁盘阵列。但是,磁盘阵列价格昂贵,磁带库又有很多缺点。最近,市面上出现了一种叫光盘库的东西。DVD光盘库,特别是DVD-RAM光盘库适用于所有大容量资料数据的存储场合,适合存储一些资料性的不经常更改的数据,比如:医院的医疗影像资料、银行等金融机构的重要票据影像资料、图书馆的书库、电视台的音像资料库等等。
DVD光盘库不仅支持传统的文件,而且可以支持各种类型的数据库,例如Oracle、Informix、SQL等,用户可以直接把数据库的表空间建立在光盘库中的DVD-RAM光盘上,可以支持数据查询和数据插入、修改、删除等操作,对于用户完全透明,感觉就如同使用硬盘一样方便。但是,目前DVD光盘库的技术似乎不太成熟,还不能作为安全性较高的数据库的存储设备。

(2)备份
光盘作为一种近十年才兴起的存储介质,同传统的磁带、软磁盘相比具有不可同日而语的优点。CD/DVD光盘由于容量大,目前单盘9.4GB,日后容量将迅速突破20GB、易保存,保存期长达30年以上,可靠性高,即使表面磨损,也可用几百元一台的修复机迅速修复,携带方便,数据交换方便,每台PC目前大部分都安装了光盘驱动器。特别DVD-RAM作为一种世界潮流的新存储介质将拥有无可限量的前途。既可以用作普通的备份介质又可用作实时的存储载体。
磁带特别是数据流磁带的单盘容量确实较大,可达到500GB,但这样一盘磁带的价格将近1000美元,同时磁带如保管不善易发霉、易磨损,接近磁体时易数据丢失,而且不同格式的磁带驱动器也不相兼容,造成了介质数据交换不便。光盘与硬盘相比也有许多无可比拟的优点,硬盘本身比较脆弱,遇到大的震动、冲击,容易损坏,光盘就可很好的解决这些问题。所以,大型图像数据库系统所有数据的备份能通过光盘完成是最好的选择。

(3)分发

目前,DVD驱动器已经成为IT行业的一种标准输入输出设备,单张DVD光盘片容量大,如DVD-ROM(4.7GB)、DVD-RAM(9.4GB),非常适合

作为分发存储介质。

四、存储图像数据的方法举例

4.1 建立具有BLOB字段的ORACLE数据库

按照如下步骤来完成各个操作:

(1)创建表空间:
CREATE TABALEESPACE VIDO_STORE
DATAFILE'C:DATABASEtest.dbf' SIZE 200M

(2)创建表:
create table part(
part_id NUMBER, 主建
part_name VARCHAR2(20),
part_image BLOB,
part_desc CLOB
part_colla BFILE
);
这个数据库第一列存储一个码,第二列存储名称,另外三列存储lob型数据。

(3)创建新的用户:如user1/pass1,赋予connect,resource权限。

(4)创建逻辑目录:
bfile类型有着特殊性,跟clob,blob不同。实际的数据文件存储在操作系统的外面:所以有两个特点:1.没有事务性控制 2.bfile是只读的,不能用dbms_lob或oracl8 oci进行修改。 为了访问外部文件,服务器需要知道文件在操作系统中的位置。下面我们建立一个目录:
create DIRECTORY utils AS '/home/utils';
utils表示目录逻辑名,'/home/utils'是实际目录。

总结:

表空间 VIDO_STORE
Oracle service_names: oradb
Oracle用户名: user1
户名密码: pass1
测试表名: test
tnsnames: oradb
逻辑目录: utils

4.2利用Visual Basic 6.0来处理大对象

在vb中处理大对象,一般可以用OO4O(oracle objects for ole)来处理。这里介绍一种不用0040处理大对象blob的方法。
下面这段程序可以将一个图像数据保存到数据库中,并可以将其从数据库读出。
程序中需要两个commandbutton
cmd1 名称 cmdsave caption 保存
cmd2 名称 cmdread caption 读取

向数据库中写入数据:
Dim OraSession As OraSession
Dim OraDatabase As OraDatabase
Dim OraDynaset As OraDynaset
Dim PartDesc As OraClob
Dim buffer As String
Dim chunksize As Long
Dim amount_written As Long

'建立OraSession对象.
Set OraSession = CreateObject("OracleInProcServer.XOraSession")

'打开数据库连接建立OraDatabase 对象.
Set OraDatabase = OraSession.OpenDatabase("ExampleDb", "scott/tiger", 0&)

'建立 OraDynaset 对象
Set OraDynaset = OraDatabase.CreateDynaset("select * from part", 0&)
Set PartDesc = OraDynaset.Fields("part_desc").Value
chunksize = 32000

重新调整buffer大小

buffer = String$(chunksize, 32)
FNum = FreeFile

'打开文件
Open "partdesc.dat" For Binary As #FNum

'设置offset和PollingAmount属性

'写入操作
PartDesc.offset = 1
PartDesc.PollingAmount = LOF(FNum)
remainder = LOF(FNum)

'锁定写入行
OraDynaset.Edit
Get #FNum, , buffer

'第一次写入操作
amount_written = PartDesc.Write(buffer, chunksize, ORALOB_FIRST_PIECE)
While PartDesc.Status = ORALOB_NEED_DATA
remainder = remainder - chunksize
If remainder < chunksize Then
piecetype = ORALOB_LAST_PIECE
chunksize = remainder
Else
piecetype = ORALOB_NEXT_PIECE
End If
Get #FNum, , buffer
amount_written = PartDesc.Write(buffer, chunksize, piecetype)
Wend
Close FNum

'更新提交
OraDynaset.Update

从数据库中读取数据:
Dim OraSession As OraSession
Dim OraDatabase As OraDatabase
Dim OraDynaset As OraDynaset
Dim PartImage As OraBlob
Dim chunksize As Long
Dim AmountRead As Long
Dim buffer As Variant
Dim buf() As Byte

'建立 OraSession对象
Set OraSession = CreateObject("OracleInProcServer.XOraSession")

'建立OraDatabase对象
Set OraDatabase = OraSession.OpenDatabase("ExampleDb","scott/tiger", 0&)

'建立OraDynaset对象
Set OraDynaset = OraDatabase.CreateDynaset("select * from part", 0&)

'从动态集中获得OraBlob
Set PartImage = OraDynaset.Fields("part_image").Value

'设置Offset和PollingAmount属性
PartImage.offset = 1
PartImage.PollingAmount = PartImage.Size
chunksize = 50000

'获得自由文件号
FNum = FreeFile

'打开文件
Open "image.dat" For Binary As #FNum

'第一次读
AmountRead = PartImage.Read(buffer, chunksize)
buf = buffer
Put #FNum, , buf

' 检查属性
While PartImage.Status = ORALOB_NEED_DATA
AmountRead = PartImage.Read(buffer, chunksize)
buf = buffer
Put #FNum, , buf
Wend
Close FNum

五、分析与展望

随着数据库管理系统功能的不断增强、性能的不断完善,将各类数据完全由数据库管理系统统一存储和管理,已成为技术发展的趋势。只

有这样,数据库管理系统的强大功能才能得到充分发挥,数据的安全性才能得到充分的保障,使得诸如数据库复制、数据的转移等许多工作,变得非常简单容易。
但是,也应当清醒的认识到对于大数据量的图像数据的存储,我们还有许多问题要进行研究。一个问题就是大对象数据的特殊操作实现,因为lob型数据是二进制的大对象,他不能简单的按照一般数据的操作符来进行计算。比如,要查出一个数据表中含有大对象的图像,如果图像很大,我们又要进行浏览,那怎么办呢?如果直接进行读取那速度是难以忍受的。只有采取分块或添加索引影像的方法,由此会带来许多需要研究的问题。
另外一个重要的问题就是对海量图像数据库的性能优化,原先的优化方法如索引优化等仍然适用,但是现在遇到了新的问题:海量图像数据是庞大的,那么对海量图像数据的操作(尤其是检索)开销巨大,那么如何降低这种开销,缩短操作时间,又是一个重要课题。

(来源:CCW)

2. 存储50万张图片的数据库

==============================
bfile类型,存储的就是文件的路径,
例子:
grant create any directory to scott;
grant create any library to scott;
create or replace directory utllobdir as 'G:oracle';
create table bfile_tab (bfile_column BFILE);
create table utl_lob_test (blob_column BLOB);

set serveroutput on

然后执行下面语句就将G:oracle目录下的Azul.jpg存入到utl_lob_test表中的blob_column字段中了。
declare
a_blob BLOB;
a_bfile BFILE := BFILENAME('UTLLOBDIR','Azul.jpg');
begin
insert into bfile_tab values (a_bfile)
returning bfile_column into a_bfile;
insert into utl_lob_test values (empty_blob())
returning blob_column into a_blob;
dbms_lob.fileopen(a_bfile);
dbms_lob.loadfromfile(a_blob, a_bfile, dbms_lob.getlength(a_bfile));
dbms_lob.fileclose(a_bfile);
commit;
end;
/
select dbms_lob.getlength(blob_column) from UTL_LOB_TEST;

本人正在做一个存储50万张图片的数据库,希望大家讨论一下,包括字段格式,存储方式,相关代码等给一个最优解。
50W张图片,如果存到数据库,那就很大了。用sql server2000存进去也可以。如果保密性要求不高的话,最好将图片存到一个目录下,数据库就存路径名就好了。
1.方案选择
如果你的数据库经常转移地方,则把图片存储在数据库中,这样方便。
如果你着重于效率,则将图片文件直接放在文件服务器中,数据库中只保存检索必须的信息,例如:文件名,目录,简介,检索关键字。
2.字段类型
图片放在数据库中的话,sql数据库字段类型用image,ACCESS用ole,其他数据库各自有各自的对应字段类型。
3.图片放在数据库中的存取

'VB/VBA中实现数据库中的文件存取

'示例数据库为ACCESS数据库,用SQL数据库的话,只需要改连接字符串
'
'*************************************************************************
'**
'** 使用 ADODB.Stream 保存/读取文件到数据库
'** 引用 Microsoft ActiveX Data Objects 2.5 Library 及以上版本
'**
'** ----- 数据库连接字符串模板 ---------------------------------------
'** ACCESS数据库
'** iConcStr = "Provider=Microsoft.Jet.OLEDB.4.0;Persist Security Info=False" & _
'** ";Data Source=数据库名"
'**
'** SQL数据库
'** iConcStr = "Provider=SQLOLEDB.1;Persist Security Info=True;" & _
'** "User ID=用户名;Password=密码;Initial Catalog=数据库名;Data Source=SQL服务器名"
'**
'*************************************************************************
'
'保存文件到数据库中
Sub s_SaveFile()
Dim iStm As ADODB.Stream
Dim iRe As ADODB.Recordset
Dim iConcStr As String

'ACCESS数据库的连接字符串
iConcStr = "Provider=Microsoft.Jet.OLEDB.4.0;Persist Security Info=False" & _
";Data Source=F:My Documents客户资料1.mdb"

'SQL数据库的连接字符串
iConcStr = "Provider=SQLOLEDB.1;Persist Security Info=True;" & _
"User ID=用户名;Password=密码;Initial Catalog=数据库名;Data Source=SQL服务器名"
'读取文件到内容
Set iStm = New ADODB.Stream
With iStm
.Type = adTypeBinary '二进制模式
.Open
.LoadFromFile "c:test.doc"
End With

'打开保存文件的表
Set iRe = New ADODB.Recordset
With iRe
.Open "表", iConc, adOpenKeyset, adLockOptimistic
.AddNew '新增一条记录
.Fields("保存文件内容的字段") = iStm.Read
.Update
End With

'完成后关闭对象
iRe.Close
iStm.Close
End Sub

'从数据库中读取数据,保存成文件
Sub s_ReadFile()
Dim iStm As ADODB.Stream
Dim iRe As ADODB.Recordset
Dim iConc As String

'数据库连接字符串
iConc = "Provider=Microsoft.Jet.OLEDB.4.0;Persist Security Info=False" & _
";Data Source=\xzc$Inetpubzjzjzj.mdb"

'打开表
Set iRe = New ADODB.Recordset
iRe.Open "tb_img", iConc, adOpenKeyset, adLockReadOnly
iRe.Filter = "id=64"

if iRe("img").ActualSize>0 Then

'保存到文件
Set iStm = New ADODB.Stream
With iStm
.Mode = adModeReadWrite
.Type = adTypeBinary
.Open
.Write iRe("img")
.SaveToFile "c:test.doc"
End With

'关闭对象
iStm.Close
End If

iRe.Close
End Sub

新建一个表 img表 列 id int , tp image
'是一个类savepicture
Dim Save_Rs As New ADODB.Recordset
Dim FileLength As Long '定义文件长度
Dim NumBlocks As Integer '定义块数量
Dim LeftOver As Integer '定义剩余数量
Dim byteData() As Byte '定义二进制数据块
Const BlockSize = 1000 '定义快大小
Dim MyFile As New FileSystemObject
Dim MyFolder As Folder
Dim i As Integer

Public Function Save(Ado As ADODB.Connection, DiskFile As String, Table As String, Col As String, id As String, Optional

IdValue As String) As Boolean
Dim strSQL As String
'没有文件路径
If Len(DiskFile) = 0 Then
Save = False
Exit Function
End If
'没有表名
If Len(Table) = 0 Then
Save = False
Exit Function
End If
'没有字段名称
If Len(Col) = 0 Then
Save = False
Exit Function
End If
'没有条件列名
If Len(id) = 0 Then
Save = False
Exit Function
End If
'没有条件列名的值
If Len(IdValue) = 0 Then
Save = False
Exit Function
End If

On Error GoTo err1
If MyFile.FolderExists("c:MyTemp") = True Then
MyFile.DeleteFolder "c:MyTemp", True
End If
MyFile.CreateFolder "c:MyTemp"

If Save_Rs.State = 1 Then Save_Rs.Close

strSQL = "select " & id & ", " & Col & " from " & Table & " where " & id & " ='" & IdValue & "'"

Save_Rs.Open strSQL, Ado, adOpenKeyset, adLockOptimistic

Open DiskFile For Binary Access Read As #1
FileLength = LOF(1)
NumBlocks = FileLength BlockSize
LeftOver = FileLength Mod BlockSize

ReDim byteData(BlockSize)
For i = 1 To NumBlocks
Get #1, , byteData()
Save_Rs.Fields(Col).AppendChunk byteData()
Next
ReDim byteData(LeftOver)
Get #1, , byteData()
Save_Rs.Fields(Col).AppendChunk byteData()
Close #1
Save_Rs.Update

If Save_Rs.State = 1 Then Save_Rs.Close

Save = True
Exit Function

err1:
Save = False
If Save_Rs.State = 1 Then Save_Rs.Close
MsgBox "保存没有成功" & Err.Description, vbQuestion

End Function

Public Function Show(Ado As ADODB.Connection, Table As String, Col As String, id As String, Optional IdValue As String)

As String
Dim strSQL As String
Dim TempF As String

'没有表名
If Len(Table) = 0 Then
Show = ""
Exit Function
End If
'没有字段名称
If Len(Col) = 0 Then
Show = ""
Exit Function
End If
'没有条件列名
If Len(id) = 0 Then
Show = ""
Exit Function
End If
'没有条件列名的值
If Len(IdValue) = 0 Then
Show = ""
Exit Function
End If

On Error GoTo err1

If MyFile.FolderExists("c:MyTemp") = True Then
MyFile.DeleteFolder "c:MyTemp", True
End If
MyFile.CreateFolder "c:MyTemp"

If Save_Rs.State = 1 Then Save_Rs.Close
strSQL = "select " & id & "," & Col & " from " & Table & " where " & id & " ='" & IdValue & "'"

Save_Rs.Open strSQL, Ado, adOpenKeyset, adLockOptimistic

FileLength = Save_Rs.Fields(1).ActualSize
TempF = "c:mytempasd.txt"

Open TempF For Binary Access Write As #2
NumBlocks = FileLength BlockSize
LeftOver = FileLength Mod BlockSize

ReDim byteData(LeftOver)
byteData() = Save_Rs.Fields(1).GetChunk(LeftOver)
Put #2, , byteData()

For i = 1 To NumBlocks
ReDim byteData(BlockSize)
byteData() = Save_Rs.Fields(1).GetChunk(BlockSize)
Put #2, , byteData()
Next
Close #2
Show = TempF
If Save_Rs.State = 1 Then Save_Rs.Close
Exit Function
err1:
Show = ""
If Save_Rs.State = 1 Then Save_Rs.Close
End Function

vb中
Dim adocon As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim DiskFile As String
Dim FileLength As Long
Dim NumBlocks As Integer
Dim LeftOver As Integer
Dim byteData() As Byte
Const BlockSize = 1000
Dim MyFile As New FileSystemObject
Dim MyFolder As Folder
Dim i As Integer
Dim TP As New SaveBin

'保存
Private Sub Command1_Click()
dim diskfile as string
CD.ShowOpen
DiskFile = CD.FileName
if len(trim(diskfiel))>0 then
TP.Save adocon, DiskFile, "img", "img", "nno", "1"
endif
End Sub

'显示
Private Sub Command2_Click()
Dim TempFile As String
TempFile = TP.Show(adocon, "image11", "img", "nno", "1")
Image1.Picture = LoadPicture(TempFile)
End Sub

3. 数据库中图片存储及读取

==============================
源作者:波波

开发环境:Window 2000、SQLServer2000、.Net Framework SDK正式版
开发语言:C#、ASP.Net
简介:数据库中图片存储及读取

说明:在ASP中,我们用Request.TotalBytes、Request.BinaryRead()来上传图片,这个可恶的BinaryRead()方法非常笨,单个文件上传倒没什么大事,单如果多个图片上专可就花大气力了…!而现在ASP.Net中将会把解决以前ASP中文件上传的种种问题,使你在ASP.Net中轻轻松松开发出功能强大的上传程序,下面大家看看例子啦。

首先在SQL Server中建立一个图片存储的数库表,SqlScript如下:

if exists (select * from dbo.sysobjects where id = object_id(N"[dbo].[image]") and OBJECTPROPERTY(id, N"IsUserTable") = 1)
drop table [dbo].[image]
GO

CREATE TABLE [dbo].[image] (
[img_pk] [int] IDENTITY (1, 1) NOT NULL ,
[img_name] [varchar] (50) NULL ,
[img_data] [image] NULL ,
[img_contenttype] [varchar] (50) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[image] WITH NOCHECK ADD
CONSTRAINT [PK_image] PRIMARY KEY NONCLUSTERED
(
[img_pk]
) ON [PRIMARY]
GO
------------------------------------------------------------
一、上传图片:
imgupload.aspx文件:
<%@ Page language="c#" Codebehind="imgupload.aspx.cs" AutoEventWireup="false" Inherits="study.uploadimage.imgupload" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>imgupload</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body>
<form enctype="multipart/form-data" runat="server" id="form1" name="form1">
文件名 <input type="text" id="imgName" runat="server" NAME="imgName">
<br>
选择文件 <input id="UploadFile" type="file" runat="server" NAME="UploadFile">
<br>
<asp:button Text="上传" runat="server" ID="Button1" />
</form>
<a href="imgview.aspx?id=1" target="_blank">看图</a>
</body>
</HTML>

codebehind文件:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO;
using System.Data.SqlClient;

namespace study.uploadimage
{
/// <summary>
/// imgupload 的摘要说明。
/// </summary>
public class imgupload : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button Button1;
protected System.Web.UI.HtmlControls.HtmlInputText imgName;
protected System.Web.UI.HtmlControls.HtmlInputFile UploadFile;

private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
}

private void Button1_Click(object sender, System.EventArgs e)
{
Stream imgStream;
int imgLen;
string imgName_value;
string imgContentType;
string imgUploadedName;

imgStream = UploadFile.PostedFile.InputStream;
imgLen = UploadFile.PostedFile.ContentLength;
imgUploadedName = UploadFile.PostedFile.FileName;
byte[] imgBinaryData=new byte[imgLen];
imgContentType = UploadFile.PostedFile.ContentType;
imgName_value = imgName.Value;

try
{
if(imgName_value.Length < 1)
{
imgName_value = GetLastRightOf("\",imgUploadedName );
}
}
catch(Exception myEx)
{
Response.Write(myEx.Message);
}

int n = imgStream.Read(imgBinaryData, 0, imgLen);
int NumRowsAffected = MyDatabaseMethod(imgName_value, imgBinaryData, imgContentType);
if(NumRowsAffected > 0)
Response.Write( "<BR> uploaded image " );
else
Response.Write ( "<BR> an error occurred uploading the image.d " );
}
public string GetLastRightOf(string LookFor,string myString)
{
int StrPos;
StrPos = myString.LastIndexOf(LookFor);
return myString.Substring(StrPos + 1);
}
public int MyDatabaseMethod(string imgName,byte[] imgbin,string imgcontenttype)
{
SqlConnection connection = new SqlConnection(Application["Test_Conn"].ToString());
string SQL="INSERT INTO Image (img_name,img_data,img_contenttype) VALUES ( @img_name, @img_data,@img_contenttype )";
SqlCommand command=new SqlCommand ( SQL,connection );

SqlParameter param0=new SqlParameter ( "@img_name", SqlDbType.VarChar,50 );
param0.Value = imgName;
command.Parameters.Add( param0 );

SqlParameter param1=new SqlParameter ( "@img_data", SqlDbType.Image );
param1.Value = imgbin;
command.Parameters.Add( param1 );

SqlParameter param2 =new SqlParameter ( "@img_contenttype", SqlDbType.VarChar,50 );
param2.Value = imgcontenttype;
command.Parameters.Add( param2 );

connection.Open();
int numRowsAffected = command.ExecuteNonQuery();
connection.Close();
return numRowsAffected;
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN:该调用是 ASP.NET Web 窗体设计器所必需的。
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.Button1.Click += new System.EventHandler(this.Button1_Click);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}

------------------------------------------------------------
二、浏览图片:
imgvies.aspx文件:
<%@ Page language="c#" Codebehind="imgview.aspx.cs" AutoEventWireup="false" Inherits="study.uploadimage.imgview" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>imgview</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
</body>
</HTML>

codebehind文件:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;

namespace study.uploadimage
{
/// <summary>
/// imgview 的摘要说明。
/// </summary>
public class imgview : System.Web.UI.Page
{

private void Page_Load(object sender, System.EventArgs e)
{
SqlConnection myDSN = new SqlConnection(Application["Test_Conn"].ToString());
myDSN.Open();

int imgid = int.Parse(Request.QueryString["id"]);
string sqlText = "SELECT img_name, img_data, img_contenttype FROM image where img_pk=" + imgid;
Trace.Write(sqlText);
SqlCommand MyCommand = new SqlCommand (sqlText, myDSN);
SqlDataReader dr =MyCommand.ExecuteReader();
if(dr.Read())
{
Response.ContentType = (dr["img_contenttype"].ToString());
Response.BinaryWrite((byte[])dr["img_data"]);
}
myDSN.Close();
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN:该调用是 ASP.NET Web 窗体设计器所必需的。
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}

这样这个程序就完成了,简单吧。当然还很多改进之处,希望大家多想想多编编一定可以写出更多的图象上传程序。

4. 系统中图片存储的解决方案

==============================
【标 题】:系统中图片存储的解决方案——将图片放在服务器上,将其路径存入数据库
【关键字】:
【来 源】:http://blog.csdn.net/icefireleaf/archive/2006/12/20/1450962.aspx

系统中图片存储的解决方案——将图片放在服务器上,将其路径存入数据库

主题  :系统中图片存储的解决方案——将图片放在服务器上,将其路径存入数据库
方案明细:1、用户选择的图片由系统将其拷贝至服务器"\servermcphoto"中,每个文件夹限制存放图片1000张
  2、用户选择图片后,系统自动检查当前使用文件夹内图片数量是否超出限制,若否,则继续使用,若是,则新建一文件夹
  3、保存图片时,系统自动检查该物品图片是否是新增图片,若是,则将当前使用文件夹中文件数加1
  4、对于以前在数据库储存的图片均已转换为图片文件放在"\servermcphotoold"中,若对其更换图片,则将新图片放入当前使用文件夹,同时将原图片文件删除
整理人 :icefireleaf
整理时间:2006-12-20
备注 :该方案是在公司改将图片存入数据库为将图片路径存入数据库之后编写,因此该方案增加了对更换从数据库中转换出来的图片时的处理,old文件夹内存放的是以前在数据库中存放,现转换出来的图片,更改存储图片方案后用户选择的图片均放在其它文件夹(即以‘n’开头的文件夹)
//----------------------------------------------------------------//
string ls_picture_path,ls_old_picturepath
string ls_docname,ls_named
int li_value
int li_filenumber,li_return
string ls_filepath,ls_filename,ls_newfile
string ls_oldfile,ls_oldfilename
int li_id
//
if dw_edit_sub.getrow()<1 then
return
end if
//
li_value=GetFileOpenName("请选择物品图片",ls_docname,ls_named,".jpg","jpg files(*.jpg),*.jpg")
if li_value=1 then
//表ormaster_file记录存放图片文件的文件夹的名称、路径、当前存放图片数量
select id,file_path,file_name,file_number
into :li_id,:ls_filepath,:ls_filename,:li_filenumber
from ormaster_file
order by id desc using sqlca;
//
if li_filenumber<=1000 then
//文件夹内图片数量未超出限制,继续使用该文件夹
ls_picture_path=ls_filepath + "" + ls_filename + "" + is_stk_no + ".jpg"
else
//文件夹内图片数量超出限制,新建一文件夹供保存图片使用,文件夹以该文件夹存满时系统已存放图片数的理论值命名
li_id=li_id + 1
ls_filename="n" + string(li_id*1000)
ls_newfile=ls_filepath + "" + ls_filename
//create new file
if not DirectoryExists(ls_newfile) then
if CreateDirectory(ls_newfile) <> 1 then
MessageBox(gs_sysmess,'创建图片保存目录'+ls_newfile+'失败﹗')
//gs_sysmess为定义的messagebox对话框的标题
return
end if
end if
//insert the record that create new file
insert into ormaster_file (file_path,file_name,file_number,creator)
values(:ls_filepath,:ls_filename,0,:gsuserid)
using sqlca;
if sqlca.sqlcode=0 then
commit;
else
rollback;
end if
//
ls_picture_path=ls_filepath + "" + ls_filename + "" + is_stk_no + ".jpg"
end if
ls_old_picturepath=dw_edit_sub.object.picture_path[1]
ls_oldfile=left(ls_old_picturepath,24)
//删除原有图片
if ls_oldfile="\servermcphotoold" then
ls_oldfilename=ls_oldfile + is_stk_no + ".jpg"
filedelete(ls_oldfilename)
end if
//copy picture to server
li_return=FileCopy(ls_docname,ls_picture_path,true)
//
if li_return=1 then
dw_edit_sub.object.picture_path[1]=ls_picture_path
p_1.picturename=ls_picture_path
//若是新加图片或更换从数据库中转换出来的图片,则当前存放图片文件夹内图片数量加1
if ls_old_picturepath='' or isnull(ls_old_picturepath) or ls_oldfile="\servermcphotoold" then
update ormaster_file set file_number=file_number + 1
where id=:li_id using sqlca;
if sqlca.sqlcode=0 then
commit;
else
rollback;
end if
end if
messagebox(gs_sysmess,"保存物品图片成功")
else
messagebox(gs_sysmess,"保存物品图片失败")
return
end if
else
messagebox(gs_sysmess,"选择物品图片失败")
return
end if

5. 基于JSP实现图片的数据库存储与显示

==============================
日期:2006-3-17 13:28:36
摘 要:本文介绍了利用JSP的编程模式如何实现图片的数据库存储和显示。
关键词:JSP、动态存取、SQL、输入输出流
收集整理:晨风教程网(http://www.Net118.com

1、引言
数据库应用程序,特别是基于WEB的数据库应用程序,常会涉及到图片信息的存储和显示。通常我们使用的方法是将所要显示的图片存在特定的目录下,在数据库中保存相应的图片的名称,在JSP中建立相应的数据源,利用数据库访问技术处理图片信息。但是,如果我们想动态的显示图片,上述方法就不能满足需要了。我们必须把图片存入数据库,然后通过编程动态地显示我们需要的图片。实际操作中,可以利用JSP的编程模式来实现图片的数据库存储和显示。

2、 建立后台数据库
假定处理的是图片新闻,那么我们可以建立相应的数据库及数据表对象。我们要存取的数据表结构的SQL脚本如下所示:
if exists (select * from dbo.sysobjects where id =
object_id(N'[dbo].[picturenews]') andOBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[picturenews]
GO
CREATE TABLE [dbo].[picturenews] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[image] [image] NULL ,
[content] [varchar] (500) COLLATE Chinese_PRC_CI_AS NULL ,
[detail] [varchar] (5000) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
表picturenews中,字段id作为标识,每存储一行数据,自动增加1。字段image用于存储图片信息,其数据类型为“image”。

3、向数据库存储二进制图片
启动Dreamweaver MX后,新建一个JSP文件。其代码如下所示。
<%@ page contentType="text/html;charset=gb2312"%>
<HTML>
<HEAD>
<TITLE>存储图片</TITLE>
</HEAD>
<body>
<!-- 下面的窗体将以Post方法,将数据传递给testimage.jsp文件 -->
<FORM METHOD=POST ACTION="testimage.jsp">
新 闻 标 题:<INPUT TYPE="text" NAME="content"><BR>
新 闻 图 片:<INPUT TYPE="file" NAME="image"><BR>
新闻内容:<TEXTAREA name="txtmail" rows="15" cols="90" style="BORDER-BOTTOM: #000000 1px solid; BORDER-LEFT: #000000 1px

solid; BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #000000 1px solid; FONT-SIZE: 9pt; HEIGHT: 200px; WIDTH: 100%"

wrap="physical" ></TEXTAREA><br>
<INPUT TYPE="submit"></form>
</body>
</HTML>
将此文件保存为InputImage.jsp文件,其中testimage.jsp文件是用来将图片数据存入数据库的,具体代码如下所示:
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*"%>
<%@ page import="java.text.*"%>
<%@ page import="java.io.*"%>
<html>
<body>
<%
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//加载驱动程序类
Connection con=DriverManager.getConnection("jdbc:odbc:denglu","sa","sa");
//建立数据库联机,其中denglu为数据库名,sa为连接数据库的帐号及密码。
Statement stmt=con.createStatement();
//建立Statement对象
String content=request.getParameter("content");
content=new String(content.getBytes("8859_1"),"gb2312");
String filename=request.getParameter("image");
filename=new String(filename.getBytes("8859_1"),"gb2312");
String detail=request.getParameter("txtmail");
detail=new String(detail.getBytes("8859_1"),"gb2312");

//获得所要显示图片的标题、存储路径、内容,并进行中文编码
FileInputStream str=new FileInputStream(filename);
String sql="insert into picturenews(content,image,detail) values(?,?,?)";
PreparedStatement pstmt=con.prepareStatement(sql);
pstmt.setString(1,content);
pstmt.setBinaryStream(2,str,str.available());
pstmt.setString(3,detail);
pstmt.execute();
//将数据存入数据库
out.println("Success,You Have Insert an Image Successfully");
%>

4、网页中动态显示图片

接下来我们要编程从数据库中取出图片,其代码如下所示。
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*"%>
<%@ page import="java.text.*"%>
<%@ page import="java.io.*"%>
<html>
<body>
<%
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//加载驱动程序类
Connection con=DriverManager.getConnection("jdbc:odbc:denglu","sa","sa");
Statement stmt=con.createStatement();
ResultSet rs=null;
//建立ResultSet(结果集)对象
int id= Integer.parseInt(request.getParameter("id"));
//获得所要显示图片的编号id,并转换为整型
String sql = "select image from picturenews WHERE id="+id+"";
//要执行查询的SQL语句
rs=stmt.executeQuery(sql);
while(rs.next()) {
ServletOutputStream sout = response.getOutputStream();
//图片输出的输出流
InputStream in = rs.getBinaryStream(1);
byte b[] = new byte[0x7a120];
for(int i = in.read(b); i != -1;)
{
sout.write(b);
//将缓冲区的输入输出到页面
in.read(b);
}
sout.flush();
//输入完毕,清除缓冲
sout.close();
}
%>
</body>
</html>
将此文件保存为testimageout.jsp文件。下一步要做的工作就是使用HTML标记:
<IMG src="testimageout.jsp?id=<%=rs.getInt("id")%>" width=100 height=100>取出所要显示的图片,其中id是所要取出图片的编号。本例中我们输出了第一个和最后一个图片信息,详细的程序代码如下所示。
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*" %>
<html>
<head>
<title>动态显示数据库图片</title>
</head>
<body>
<%
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con=DriverManager.getConnection("jdbc:odbc:denglu","sa","sa");
Statement stmt=con.createStatement();
String sql=new String();
sql= "select * from picturenews";
ResultSet rs=stmt.executeQuery(sql);
rs.last();
//将指针移至最后一条记录
%>
<table>
<tr><td><IMG height=99 src="testimageout.jsp?id=1" width=136></td>
//取出第一个图片
<td><IMG height=99 src="testimageout.jsp?id=<%=rs.getInt("id")%>" width=136></td>
//取出最后一个图片
</tr></table>
</body>
</html>
以上WEB应用程序在Windows 2000 Professional/SQL Server 2000/ Apache Tomcat 4.0/JDK 1.4 JAVA环境下调试通过。


6. Image Storage -- Better to store them on the Filesystem or in the MySQL DB

==============================
Posted by: Marco Schierhorn (IP Logged)
Date: November 24, 2005 03:23AM

we´ve large amount of Images ( nearly 2 GB ). And they will increase, cause or Editors will submit several articles a day where they´re allowed to upload some new pics.
So, whats the better solution ? To store them in the filesystem ( with only the path in the db ) or to store them as BLOB´s in the DB ( MySQL ). When we decide to store them in the DB, we´ve thought about splitting larger Images ( > 64 K ) into several pieces into the db. So, we´ve a simulated "streaming". I´ve seen it at [php.dreamwerx.net].
So, any recommendation is very welcome.I would really appreciate any of them.

Marco

--------------
Posted by: Jay Pipes (IP Logged)
Date: November 24, 2005 11:29AM

If you aren't running a web server farm (where images are stored on multiple web servers), let the operating system store the image files, as the OS is better suited for such things. Not that it can't be done in MySQL. It certainly can, but it's more complicated and less efficient than if you can store things in the filesystem. The only times I bother with storing image type files in the SQL server is if there are certain security restrictions, there is a distributed web server farm that needs a single source of image files, or if the client absolutely insists on it... :)

Jay Pipes
Community Relations Manager, North America, MySQL Inc.

--------------
Posted by: James Day (IP Logged)
Date: November 24, 2005 08:31PM

Opinions vary, with good reasons for the different views. Jay gave one common view. There are many merits to that approach. One drawback of using file system storage is the comparative difficulty of replicating it to a server farm to spread the load. This doesn't matter initially but as load grows and you possibly start to have a need for more than one server, possibly at several different sites, the benefits of the replication built into the database start to become more interesting.

In the database server version, you might use a different database server, possibly on the same physical machine initially. The different database makes it easy to have different backup schedules and easy to split later. As load rises you could switch to a different machine or to a set of them. For example, you might consider sets of three plain web servers running Apache as their main job and give them a small MySQL instance just to serve some images and make use of their disk drives for something. Three because that makes recovery from failure easy - if one dies, another can keep serving while you use the third as the source for a copy to a replacement machine. If you use InnoDB you don't need to be

greatly concerned about the chunk size - I know of a case of storage of 64MB video chunks for broadcast distribution. I do recommend not storing them with the metadata or other row data, because that substantially increases the time for typical operations, which are unlikely to need the image itself.

Alternatively, investigate the very interesting MogileFS ( [www.danga.com] ) from the LiveJournal people. It's a very scalable system using MySQL to record where distributed, redundant copies of images are stored. It seems to be a very capable system, with nice features like automatic load balancing recently added.

Better still, build an object-based location specifier which lets you change between any of these methods as circumstances change or you introduce new storage options.

At two gigabytes, my guess is that either MogileFS or the database will be the option which makes your life easiest, through increased performance and availability.

There's no one, perfect, right answer which covers all cases. It's always a matter of considering the situation and the relative merits of each approach in the circumstances involved.

James Day
Support engineer, MySQL AB

--------------
Posted by: Richard Hillström (IP Logged)
Date: May 05, 2006 05:36AM

I run a mysql database to store and search among my personal pictures and other stuff i want to have online. One table is used for all the metadata and one table is used to hold just the files as large blobs and also a third table for picture thumbnails as normal blobs.

I currently have 10GB data, (7 000 files, mostly pictures) in the database, website is running on Apache/PHP/Ubuntu. And all on a laptop, the fastest I could find, but still a laptop! The trick is to use the metadata table as much as possible, and just use the pointer in that table to get the data from the blob tables. And ofcourse indexes, without them you're lost. Insert and selects are as fast as the harddisk can handle. When searching for files i get the thumbnail pictures from the database as previews.

Searching and displaying 3000 thumbnails (which are approx 2-3k in size), takes below 10 seconds. I did a test trying to crash the database by inserting all my pictures but in multiple copies, ended up with over 30GB blob table and approx 20 000 rows. Still no problem at all, selects and getting the data are just as fast as before.

So for me storing in the database works great.

BR
Richard


7. store image files in MySQL or flat file

==============================
http://techrepublic.com.com/

I want to compare 2 image storage systems, one in MySQL and one in a flat file format:-

1) performance
As I read some articles / forums that someone says if the image stored in MySQL to be retreive more faster than stored in a flat file and someone have different ideas.

2) backup
Someone said it is easiler to backup the image files stored in a flat file than stored in MySQL database.

3) security
I would like to hear more about the 3 points that I pointed out since I will install the shopping cart in my website. Once I select the image storage method, then I cannot return it back.

Pleaes give advise.

Andy Ng
Posted: 04/18/2005 @ 01:03
Job Role: Networking / LAN Administration
Location: Hong Kong, HK
Member since: 02/27/2002

------------
1) the db performance takes a hit when you store the images in it. they increase th size for no gain. just store the path to the images.

2) to backup the db, you need to be able to pull a copy of the sql file or else, dump contents to a file with full inserts. if you do not have that access then you can't backup anything in the db. ( unlikley, hosting companies do give that access )

3) images will be copied of people want them. there is no 100% effective means to stop it.
there is no risk to site security by having images in the site space instead of the db, the space should not be world writable anyways.

Posted: 04/21/2005 @ 03:49
Jaqui 124
Job Role: Software / Applications Development
Location: Vancouver, BC
Member since: 08/25/1999

------------
there are a few methods to interfere with downloading images from sites.

javascript based methods are useless, all that needs to be done is turn javascript off and security broken.

best method:
slice image and set as background in table.
in forground, place a transparent gif.
when people try to save, they get the gif.
if they get the backgrounds, then they have to re-assemble the image correctly to be able to use it.

Posted: 06/04/2005 @ 19:03
Jaqui 124
Job Role: Software / Applications Development
Location: Vancouver, BC
Member since: 08/25/1999

No comments: