各种类型文件在SQL Server
中存储的解决方案
数据的持久化是开发的基础性工作,我们不可避免要将各种的类型文件持久化,关于文件(或是大对象)的存储,我在我的blog http://www.cnblogs.com/supercode/articles/156744.html谈过
今天我们从设计的角度来实现这功能,从本文中您将了解道以下内容
l SQL Server中的数据类型
l 数据表,存储过程的设计
l 逻辑层实现各种类型文件的自动转化
l DataGrid中的自定义文件列表显示方式,以及从服务端将文件发送客户端时的一些技巧
1. Sql server
中的数据类型
Unicode 字符串
nchar
固定长度的 Unicode 数据,最大长度为
4
,
000
个字符。
nvarchar
可变长度 Unicode 数据,其最大长度为
4
,
000
字符。sysname 是系统提供用户定义的数据类型,在功能上等同于 nvarchar(
128
),用于引用数据库对象名。
ntext
可变长度 Unicode 数据,其最大长度为
2
^
30
-
1
(
1
,
073
,
741
,
823
) 个字符。
二进制字符串
binary
固定长度的二进制数据,其最大长度为
8
,
000
个字节。
varbinary
可变长度的二进制数据,其最大长度为
8
,
000
个字节。
image
可变长度的二进制数据,其最大长度为
2
^
31
-
1
(
2
,
147
,
483
,
647
) 个字节。
要想更加详细的数据类型请查阅Sql Server自带的帮助文件,之所以把这几个罗列出来是因为,网上很多朋友经常问到底用binary还是用image作为存储的数据类型,很显然,应该用image,因为很少有文件小于8K的,除非是网络图像(jpeg,gif,png)
2.
数据表,
存储过程的设计
(1)创建表
下面是创建表的Sql
图1存储文件的表
(2)创建存储过程
1. 写入数据库的存储过程
/**/
/************************************************************
* Purpose: Test For UpLoad File To Sql Server *
* Author: 登峰 *
* Blog: http://www.cnblogs.com/supercode *
* Date: 2005-6-12 *
*****************************************************************/
CREATE
proc
SetFileToDB
@_FileName
as
nvarchar
(
255
)
=
null
,
@_FileType
as
nvarchar
(
50
)
=
null
,
@_FileContent
as
image
=
null
,
@_FileSize
as
int
=
null
,
@_FileUploader
as
nvarchar
(
50
)
=
null
,
@_UploadDate
as
datetime
=
null
,
@_Icon
as
nvarchar
(
50
)
=
null
AS
--
声明SQL变量
declare
@CreateTabSql
as
nvarchar
(
100
);
--
声明错误处理变量
declare
@CurrentError
int
--
事务开始
BEGIN
TRANSACTION
--
插入表
insert
into
FileLib(FName,FileType,FileContent,FileSize,FileUploader,UploadDate,Icon)
values
(
@_FileName
,
@_FileType
,
@_FileContent
,
@_FileSize
,
@_FileUploader
,
@_UploadDate
,
@_Icon
)
select
@CurrentError
=
@@Error
IF
@CurrentError
!=
0
BEGIN
GOTO
ERROR_HANDLER
END
--
事务结束
COMMIT
TRANSACTION
--
成功的话返回0
RETURN
0
ERROR_HANDLER:
ROLLBACK
TRANSACTION
RETURN
@CurrentError
GO
3. 逻辑层实现各种类型文件的自动转化
引用这层的目的就是简要说明一下层次的问题,本来数据层也独立出来,但这文章的目的不在于此,所以附带而过,而且这逻辑层也非常简单,为了方便起见,把相关的类和操作都放在一起文件里
(3.1)定义文件实体类
class
FileEntity

{
private int _ID;
private string _FileName;
private string _FileType;
private byte[] _FileContent;
private int _FileSize;
private string _FileUploader;
private DateTime _UploadDate;
private string _Icon;

属性#region 属性
public int ID

{

set
{_ID=value;}

get
{ return ID;}
}
public string FileName

{

set
{_FileName=value;}

get
{ return _FileName;}
}
public string FileType

{

set
{_FileType=value;}

get
{ return _FileType;}
}
public byte[] FileContent

{

set
{_FileContent=value;}

get
{ return _FileContent;}
}
public int FileSize

{

set
{_FileSize=value;}

get
{ return _FileSize;}
}
public string FileUploader

{

set
{_FileUploader=value;}

get
{ return _FileUploader;}
}
public DateTime UploadDate

{

set
{_UploadDate=value;}

get
{ return _UploadDate;}
}
public string Icon

{

set
{_Icon=value;}

get
{ return _Icon;}
}
#endregion
}
(3.2)扩展名和图标的处理
要想在列表里实现哪种类型的文件对应哪种图标,这需求关关联,数据库中Icon就是来保存文件的扩展名的,看下面两个处理方法
/**/
///
/// 从本地的全名路径(含文件名)中获取文件名
///
/// 全名路径(含文件名)
/// 文件名
private
string
GetFileName(
string
path)

{
int index=path.LastIndexOf("//");
return path.Substring(index+1);
}

/**/
///
/// 从本地的全名路径(含文件名)中获取文件的扩展名
///
/// 全名路径(含文件名)
/// 文件的扩展名
private
string
GetExteName(
string
path)

{
int index=path.LastIndexOf(".");
return path.Substring(index+1);
}
4. 页面的实现
4.1 页面HMTL的描述
确信你设定了Form的encType属性为multipart/form-data。显示文件列表的关键是DataGird,先看看他的描述
<
asp:DataGrid
id
="DataGrid1"
AutoGenerateColumns
="False"
style
="Z-INDEX: 101; LEFT: 120px; POSITION: absolute; TOP: 88px"
runat
="server"
Width
="664px"
Height
="152px"
BorderColor
="#CC9966"
BorderStyle
="None"
BorderWidth
="1px"
BackColor
="White"
CellPadding
="4"
>
<
FooterStyle
ForeColor
="#330099"
BackColor
="#FFFFCC"
>
FooterStyle
>
<
SelectedItemStyle
Font-Bold
="True"
ForeColor
="#663399"
BackColor
="#FFCC66"
>
SelectedItemStyle
>
<
ItemStyle
ForeColor
="#330099"
BackColor
="White"
>
ItemStyle
>
<
HeaderStyle
Font-Bold
="True"
ForeColor
="#FFFFCC"
BackColor
="#990000"
>
HeaderStyle
>
<
PagerStyle
HorizontalAlign
="Center"
ForeColor
="#330099"
BackColor
="#FFFFCC"
>
PagerStyle
>
<
Columns
>
<
asp:TemplateColumn
HeaderText
="图标"
>
<
ItemTemplate
>
<
img
src
='images/<%#
(DataBinder.Eval(Container.DataItem, "Icon").ToString()) %
>
.gif' />
ItemTemplate
>
asp:TemplateColumn
>
<
asp:TemplateColumn
HeaderText
="文件名"
>
<
ItemTemplate
>

<
a
href
=WebForm1.aspx?fid=<%#
DataBinder.Eval(Container.DataItem,"FID")%
>
>
<%
# DataBinder.Eval(Container.DataItem, "FName")
%>
a
>
ItemTemplate
>
asp:TemplateColumn
>
<
asp:TemplateColumn
HeaderText
="内部类型"
>
<
ItemTemplate
>
<
asp:Label
id
="FileName"
runat
="server"
Text
='<%#
DataBinder.Eval(Container.DataItem, "FileType") %
>
' />
ItemTemplate
>
asp:TemplateColumn
>
<
asp:TemplateColumn
HeaderText
="文件大小"
>
<
ItemTemplate
>
<
asp:Label
id
="FileSize"
runat
="server"
Text
='<%#
DataBinder.Eval(Container.DataItem, "FileSize","{0:N}") %
>
'/>
ItemTemplate
>
asp:TemplateColumn
>
<
asp:TemplateColumn
HeaderText
="上传者"
>
<
ItemTemplate
>
<
asp:Label
id
="FileUploader"
runat
="server"
Text
='<%#
DataBinder.Eval(Container.DataItem, "FileUploader") %
>
'/>
ItemTemplate
>
asp:TemplateColumn
>
<
asp:TemplateColumn
HeaderText
="上传日期"
>
<
ItemTemplate
>
<
asp:Label
id
="UploadDate"
Text
='<%#
DataBinder.Eval(Container.DataItem, "UploadDate","{0:yyyy-MM-dd}") %
>
' runat="server"/>
ItemTemplate
>
asp:TemplateColumn
>
Columns
>
asp:DataGrid
>
4.2 把DB中的文件显示在DataGrid上,因为仅仅是显示,所以FileContent字段没有必要读出来,而且在这里我们自定义一个DataTable来绑定到DataGrid中,请看下面的代码
1
/**/
///
2
/// 从数据库中读取文件信息显示在DataGrid上
3
///
4
private
void
BindGrid()
5

{
6
7
string SelectCommand="select ID,FName,FileType,FileSize,FileUploader,UploadDate,Icon from FileLib";
8
9
SqlConnection myConnection=null;
10
try
11
{
12
myConnection = new SqlConnection(ConnectionString);
13
myConnection.Open();
14
15
SqlCommand sqlcmd=new SqlCommand(SelectCommand,myConnection);
16
17
18
SqlDataReader ddr=sqlcmd.ExecuteReader();
19
20
DataTable dt = new DataTable();
21
DataRow dr;
22
dt.Columns.Add(new DataColumn("FileType", typeof(string)));//0
23
dt.Columns.Add(new DataColumn("FName", typeof(string)));//1
24
dt.Columns.Add(new DataColumn("FileSize", typeof(string)));//2
25
dt.Columns.Add(new DataColumn("FileUploader", typeof(string)));//3
26
dt.Columns.Add(new DataColumn("UploadDate", typeof(string)));//4
27
dt.Columns.Add(new DataColumn("Icon", typeof(string)));//5
28
dt.Columns.Add(new DataColumn("FID", typeof(string)));//6
29
30
31
while(ddr.Read())
32
{
33
dr=dt.NewRow();//新一行
34
dr[0]=ddr["FileType"].ToString();
35
dr[1]=ddr["FName"].ToString();
36
dr[2]=ddr["FileSize"].ToString();
37
dr[3]=ddr["FileUploader"].ToString();
38
dr[4]=ddr["UploadDate"].ToString();
39
dr[5]=ddr["Icon"].ToString();
40
dr[6]=ddr["ID"].ToString();
41
dt.Rows.Add(dr);
42
43
}
44
//绑字到DataGrid
45
DataGrid1.DataSource = new DataView(dt);
46
DataGrid1.DataBind();
47
}
48
catch (System.Exception Ex)
49
{
50
Response.Write(Ex.Message+Ex.StackTrace);
51
}
52
finally
53
{
54
myConnection.Close();
55
}
56
}
4.3 上传文件至Sql Server 数据库
IIS对上传的大小是很限定,当然这在web.config中配置,具体的这里不详述,再查阅相关的资料,我们先把页面级的字段放在文件实体类中,再将实体类传到逻辑层来处理,三层的原理也是如此,下面是初始化提交代码
void InitEntity()
{
//读取相关值
FileEntity fe=new FileEntity();
Stream DataStream = File1.PostedFile.InputStream; //文件流
fe.FileSize=File1.PostedFile.ContentLength; //文件长度
byte[] bdata = new byte[fe.FileSize];
int n = DataStream.Read(bdata,0,fe.FileSize); //全部读取缓冲,n代表实际读取字节数
fe.FileContent =bdata;
fe.FileName=GetFileName(File1.PostedFile.FileName);//全称
fe.FileUploader=this.txtUploader.Text;
fe.FileType=File1.PostedFile.ContentType;
fe.UploadDate=DateTime.Now;
fe.Icon=GetExteName(File1.PostedFile.FileName);
//开始写入数据库
UpLoadFileToDB(fe);
}
|
下面的代码是写入数据库的代码
/**/
///
/// 上传文件至数据库
///
private
void
UpLoadFileToDB(FileEntity fe)

{
SqlConnection myConnection =null;
try

{
myConnection = new SqlConnection(ConnectionString);
SqlCommand command = new SqlCommand("SetFileToDB", myConnection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@_FileName", fe.FileName);
command.Parameters.Add("@_FileType", fe.FileType);
command.Parameters.Add("@_FileContent",fe.FileContent);
command.Parameters.Add("@_FileSize", fe.FileSize);
command.Parameters.Add("@_FileUploader", fe.FileUploader);
command.Parameters.Add("@_UploadDate", fe.UploadDate);
command.Parameters.Add("@_Icon", fe.Icon);
myConnection.Open();
int Result=command.ExecuteNonQuery();
BindGrid();
}
catch(Exception ex)

{
Response.Write(ex.Message+ex.StackTrace);
}
finally

{
myConnection.Close();
}
}
4.4 下载文件时的处理过程
当然这是也是最关键的,不然没意义了,当然这会涉及到小问题,比如,下载时的中文乱码问题,对jpeg或word文件是下载还是直接在IE中打开等问题,这些问题我都已在下面的代码中解决
/**/
///
/// 将文件发给客户端,fid是本面Load时从Request读取的
///
/// 文件编号
void
DownLoadFile(
int
fid)

{
SqlConnection myConnection=null;
string strDownloadSql="select FileType,FName,FileContent from FileLib where ID="+fid;
try

{
myConnection = new SqlConnection(ConnectionString);
myConnection.Open();
SqlCommand sqlcmd=new SqlCommand(strDownloadSql,myConnection);
SqlDataReader ddr=sqlcmd.ExecuteReader();
if(ddr.Read())

{
Response.ContentType = ddr["FileType"].ToString();
Response.AddHeader("Content-Disposition", "attachment;FileName="+HttpUtility.UrlEncode(ddr["FName"].ToString(),System.Text.Encoding.UTF8 ));
Response.BinaryWrite( (byte[]) ddr["FileContent"] );
Response.End();
}
}
catch(Exception ex)

{
Response.Write(ex.Message+ex.StackTrace);
}
finally

{
myConnection.Close();
}
}
好,文章到这也即将结束,最后我们来看看最终的DEMO效果
图2 文件上传后
下面看看下载时的效果
图3 文件下载
到此大功告成,当然您也可以在这基础之上把功能再加大,比如实现编辑,目录方式等,今天是周未,登峰祝您周未愉快!