第26章 文件批量上传系统
本章视频教学录像:14分钟
文件上传是开发项目中必不可少的功能,也是用户和网站管理者之间的交流工具。本章使用JavaScript介绍如何动态地添加上传文件。
本章要点(已掌握的在方框中打钩)
□ 了解批量上传的原理
□ 创建数据库和数据表
□ 设计系统前台页面
□ 设计批量上传代码
□ 将批量上传系统添加至已有网站中
26.1 系统分析
本节视频教学录像:2分钟
网站建成后,用户希望能够保存一些文件信息到网站上,系统管理员由于工作的需要,经常要上传很多图片文件到系统中,如果能够批量上传信息,则能更好地满足需要。
本章使用ASP.NET和SQL Server 2008来创建一个批量上传系统。
26.1.1 系统目标
本系统需要实现的目标有以下4点。
⑴根据需要添加上传控件。
⑵根据需要删除上传控件。
⑶保存上传信息。
⑷显示上传信息。
26.1.2 系统原理
当用户需要上传文件时,单击【添加附件】按钮可以动态地增加上传控件,单击【删除】按钮可以移除多余的上传控件。用户选择好要上传的文件,单击【上传】按钮即可保存所有选定的文件信息到网站中。
26.1.3 技术要点
如何获取上传的文件个数呢?
当用户上传文件时,本系统通过Request的Files属性获得请求中的文件信息,然后根据获得的文件信息的个数逐个保存到网站中。如果用户未选择上传文件,那么得到的文件名肯定是空字符串,则不保存该条信息。代码将在26.3.4小节中介绍。
26.2 数据库分析及设计
本节视频教学录像:1分钟
根据分析,本系统需要用到数据库来存储上传文件保存的数据,所以在系统开发之前,首先要进行数据库的设计。
26.2.1 数据库分析
在批量上传系统中,需要保存用户上传的文件信息,包括文件名、保存的位置、保存的时间、文件的类型等信息。
26.2.2 创建数据库
在SQL Server 2008中创建数据库的具体步骤如下。
⑴选择【开始】【所有程序】【Microsoft SQL Server 2008】【SQL Server Management Studio】,以【SQLServer身份验证】模式登录。
⑵在【对象资源管理器】窗口中的【数据库】节点上右击,在弹出的快捷菜单中选择【新建数据库】菜单项,弹出【新建数据库】对话框。
⑶在【数据库名称】文本框中输入“ch26DataBase”,在【数据库文件】列表框中使用默认设置“ch26DataBase”和“ch26DataBase_log”文件的路径,然后单击【确定】按钮,即可完成ch26DataBase数据库的创建。
26.2.3 创建数据表
本实例中需要一张记录上传文件相关信息的表,下面来创建数据表。
⑴在【对象资源管理器】中展开ch26DataBase节点,右击【表】节点,在弹出的快捷菜单中选择【新建表】菜单项。
⑵在打开的表编辑窗口中,按照下表进行输入。
⑶在【dbo.Table_1*】选项卡上右击,在弹出的快捷菜单中选择【保存】菜单项,弹出【选择名称】对话框,在【输入表名称】文本框中输入“tabUploadFile”,然后单击【确定】按钮,即可完成表的创建。
26.3 系统设计
本节视频教学录像:6分钟
在26.1.1小节中,我们已经提出了批量上传系统需要实现的4个目标,所以在系统设计时,需要设计4个模块来实现各个目标。
26.3.1 系统页面设计
首先设计系统的页面,页面中需要有【添加附件】按钮、【上传】按钮,上传控件在单击【添加附件】按钮时显示。
⑴在Visual Studio 2010中,新建【语言】为【Visual C#】的ASP.NET空网站,添加一个默认的Default.aspx页面。
⑵在Default.aspx页面的设计视图中,如图所示设计系统界面,所用控件及属性设置如表所示。
⑶为了动态添加上传控件,需要使用JavaScript脚本来实现此功能。在源视图的<title>< /title>标签对之后添加脚本函数(代码26-1.txt)。
01 <script language="javascript" type="text/javascript">
02 function AddFileCtrol() { //添加一个上传控件
03 var innerDiv=document.createElement("div"); //新建一个Div元素
04 document.getElementById("dv1").appendChild(innerDiv) //添加到Div元素中
05 var fileCtrol=document.createElement("input"); //建立 input元素
06 fileCtrol.name= "upFile"; //设置元素的名称
07 fileCtrol.type= "file"; //设置元素的类型
08 innerDiv.appendChild(fileCtrol); //添加到 fileCtrol元素
09 var btnCtrol=document.createElement("input"); //建立 input元素
10 btnCtrol.name= "btnDelete"; //设置元素的名称
11 btnCtrol.type= "button"; //设置元素的类型
12 btnCtrol.setAttribute("value", "删除 "); //设置元素的显示文字
13 btnCtrol.onclick= function() {DeleteFileCtrol(this.parentNode) }; //绑定删除上传控件函数到onclick事件
14 innerDiv.appendChild(btnCtrol); //添加到Div元素
15 }
16 function DeleteFileCtrol(obj) { //删除对应上传控件函数
17 document.getElementById("dv1").removeChild(obj);
18 }
19 < /script>
⑷在网站中添加1个名为“UserRegist.aspx”的Web页面,用于显示上传的文件信息以供下载。在UploadFileList.aspx页面的设计视图中,如图所示设计系统界面,所用控件及属性设置如表所示。
26.3.2 配置网站的Web.config
数据库和系统页面都设计好了,如何将它们连接起来呢?这就需要通过配置系统的Web.config文件来连接数据库。
在【解决方案资源管理器】中双击Web.config文件,打开Web.config的代码窗口,然后将<connectionStrings>和< /connectionStrings>之间的代码更换为以下代码。
<add name="ch26DataBase" connectionString="Data Source= localhost;Initial Catalog=ch26DataBase;User ID=sa;Password=123"/>
【代码详解】
此段代码的作用是添加一个数据库的连接字符串,“localhost”表示要连接当前本机的数据库,读者也可设定为数据库所在服务器的IP地址。数据库的名称为“ch26DataBase”。
26.3.3 数据库连接代码设计
在系统保存上传信息时要连接数据库,结束时要关闭此连接。所有的相关数据操作使用一个公共数据类来实现。
新建一个类文件,命名为“DataClass.cs”,添加GetSqlServerConn方法,用于获得数据库连接(代码26-2.txt)。
01 private SqlConnection GetSqlServerConn()
02 {
03 SqlConnection sqlConn; //定义SQlServer连接对象
04 string strConn = WebConfigurationManager.ConnectionStrings["ch26DataBase"].ConnectionString; //读取Web.config配置文件的ConnectionString节点获取连接字符串
05 sqlConn= new SqlConnection(strConn); //生成数据库连接对象
06 sqlConn.Open(); //打开数据库连接
07 return sqlConn; //返回数据库连接对象以供调用
08 }
继续添加CloseSqlServerConn方法,用于关闭数据库连接(代码26-3.txt)。
01 private void CloseSqlServerConn(SqlConnection sqlConn)
02 {
03 if (sqlConn.State==ConnectionState.Open) //如果数据库连接处于关闭状态,则打开此连接
04 {
05 sqlConn.Close();
06 }
07 }
26.3.4 保存上传信息代码设计
有了数据库连接,我们就可以通过它保存上传文件的信息,系统可以将此信息保存至数据库。在公共类中继续添加SaveFilesInfo方法来保存上传文件的信息内容(代码26-4.txt)。
01 public bool SaveFilesInfo(HttpFileCollection fileColl)
02 {
03 SqlConnection sqlConn;
04 SqlCommand sqlComm;
05 string strComm=@"insert into
06 tabUploadFile(NewFileName,OldFileName,SaveAddress,UploadTime,TypeName)
07 values(@NewFileName,@OldFileName,@SaveAddress,@UploadTime,@ TypeName)";
08 sqlConn=GetSqlServerConn(); //调用方法获取数据库连接
09 SqlTransaction sqlTran= sqlConn.BeginTransaction(); //开始数据库事务
10 StringBuilder sbFileName;
11 try
12 {
13 sqlComm= new SqlCommand(strComm, sqlConn);
14 sqlComm.Transaction= sqlTran; //指定命令要使用的事务
15 for (int i= 0; i< fileColl.Count; i++) //循环保存上传信息
16 {
17 if (!String.IsNullOrEmpty(fileColl[i].FileName))
18 {
19 sbFileName= new StringBuilder(); //使用年月日时分秒毫秒生成文件名
20 sbFileName.Append(DateTime.Now.Year);
21 sbFileName.Append(DateTime.Now.Month);
22 sbFileName.Append(DateTime.Now.Day);
23 sbFileName.Append(DateTime.Now.Hour);
24 sbFileName.Append(DateTime.Now.Minute);
25 sbFileName.Append(DateTime.Now.Second);
26 sbFileName.Append(DateTime.Now.Millisecond);
27 sbFileName.Append(Path.GetExtension(fileColl[i].FileName));
28 sqlComm.Parameters.Clear(); //清空以前参数,指定当前SQL命令参数
29 sqlComm.Parameters.AddWithValue("@NewFileName", sbFileName.ToString());
30 sqlComm.Parameters.AddWithValue("@OldFileName", Path.GetFileName(fileColl[i].FileName));
31 sqlComm.Parameters.AddWithValue("@SaveAddress", System.Web.HttpContext.Current.Server.MapPath("~/Upload/"));
32 sqlComm.Parameters.AddWithValue("@UploadTime",DateTime.Now);
33 sqlComm.Parameters.AddWithValue("@TypeName", Path.GetExtension (fileColl[i].FileName));
34 sqlComm.ExecuteNonQuery(); //执行SQL命令
35 fileColl[i].SaveAs(System.Web.HttpContext.Current.Server.MapPath("~/Upload/") +sbFileName.ToString()); //保存对应的文件到服务器
36 }
37 }
38 sqlTran.Commit(); //提交事务保存数据
39 return true;
40 }
41 catch (Exception ex)
42 {
43 sqlTran.Rollback(); //发生异常回滚事务
44 CloseSqlServerConn(sqlConn); //关闭数据库连接
45 return false;
46 }
47 }
26.3.5 获取上传文件信息代码设计
对用户上传的文件信息可以随时下载,因此需要将上传的文件信息显示给用户。在公共类中继续添加GetFilesInfo方法来获取上传文件的信息内容(代码26-5.txt)。
01 public DataTable GetFilesInfo()
02 {
03 SqlConnection sqlConn;
04 SqlDataAdapter sqlAdpt;
05 DataTable dtFilesInfo;
06 string strComm = "select NewFileName,OldFileName,SaveAddress,UploadTime,TypeName from tabUploadFile";
07 try
08 {
09 sqlConn=GetSqlServerConn();
10 sqlAdpt= new SqlDataAdapter(strComm, sqlConn); //使用数据适配器读取数据
11 dtFilesInfo= new DataTable();
12 sqlAdpt.Fill(dtFilesInfo); //填充数据到DataTable
13 return dtFilesInfo;
14 }
15 catch (Exception ex)
16 {
17 throw ex;
18 }
19 }
26.3.6 保存上传文件的事件代码设计
用户选择好上传的文件之后,单击【上传】按钮可以保存文件信息。在Default.aspx设计页面双击“上传”按钮自动添加事件btnUpload_Click方法,添加以下代码(代码26-6.txt)。
01 protected void btnUpload_Click(object sender,EventArgs e)
02 {
03 HttpFileCollection fileColl=Request.Files; //获取当前请求中所有的文件信息
04 DataClass dc= new DataClass();
05 bool bResult= false; //判断保存是否成功
06 try
07 {
08 bResult=dc.SaveFilesInfo(fileColl);
09 }
10 catch (Exception ex) { }
11 if (bResult)
12 {
13 Response.Write("<script>alert('文件保存成功! ')< /script>");
14 }
15 else
16 {
17 Response.Write("<script>alert('文件保存失败! ')< /script>");
18 }
19 }
26.3.7 下载上传文件的事件代码设计
本系统将用户上传的文件信息显示在页面上,当单击对应信息的时候,可以下载该文件。在UploadFileList.aspx设计页面空白处双击添加Page_Load窗体事件,添加以下代码(代码26-7.txt)。
01 protected void Page_Load(object sender,EventArgs e)
02 {
03 if (!IsPostBack) //第一次打开页面绑定文件信息到GridView控件
04 {
05 this.grdFileList.DataSource= new DataClass().GetFilesInfo(); //绑定文件信息数据
06 this.grdFileList.DataBind();
07 }
08 }
由于页面显示文件名的字段NavigateUrl属性使用了一个处理方法GetFileUrl生成保存在服务器端的文件名,所以需要继续添加GetFileUrl方法(代码26-8.txt)。
01 public string GetFileUrl(string strAddress, string strFileName)
02 {
03 return strAddress+ strFileName; //获取服务器保存的文件实际路径和实际文件名
04 }
26.4 运行系统
本节视频教学录像:2分钟
下面来看系统运行的效果。
⑴按【F5】或【Ctrl+F5】快捷键,在浏览器中运行该程序,页面中将显示【添加附件】和【上传】按钮。
⑵单击【添加附件】按钮,可自动生成一个上传控件和一个【删除】按钮。连续单击可生成多个上传控件,单击【删除】按钮可删除对应的上传控件。
⑶单击【上传】按钮,将文件信息保存到数据库中并将文件保存至服务器,保存完成后返回提示信息,如图所示。
⑷用户可以访问UploadFileList.aspx页面查看上传的文件信息,如图所示。
⑸用户单击对应的文件名或目标执行另存为操作可以下载该文件。
26.5 在我的网站中应用本系统
本节视频教学录像:1分钟
将Default.aspx页面中相关的代码拷贝至一个已存在页面作为部分代码,或者将该页面改名后作为一个链接添加至已有页面中即可。其余相关代码文件无需改动,拷贝至已有网站对应的位置就可以运行。应该根据具体情况修改数据库表,可以在其中添加一个用户字段,以区分每个用户上传的文件信息。更多的细化功能读者可自行练习,这里不再赘述。
26.6 开发过程中的常见问题及解决方式
本节视频教学录像:2分钟
开发过程中应该注意以下几点。
⑴使用JavaScript脚本语言时,读者要熟练掌握使用脚本语言生成Html控件的方法,特别要注意脚本事件是如何绑定到对应按钮上的。
⑵保存上传文件的时候,一般情况下都要对文件进行改名保存,以防止重名文件的出现。常用的方法是取得当前系统时间加上毫秒级的时间组成新的文件名。
⑶在GridView显示数据的时候,我们会发现保存的文件路径和文件名称需要组合成一个字段显示,这时需要一个转换函数,即代码中提到的GetFileUrl (string strAddress, string strFileName)函数,此函数将两个字段信息组合后重新绑定到GridView控件上。需要注意的是此函数一定要是Public类型的,否则前端页面代码不能使用此函数。
⑷在GridView显示数据的时候,我们还会发现日期字段显示的时候会出现年月日时分秒的格式,为了去除多余的时间,需要格式化该字段,需要使用DataFormatString="{0:yyyy-MM-dd}",使日期只显示年月日的格式。但是需要注意的是,一定要把HtmlEncode属性设置为False,否则DataFormatString属性不起作用。
⑸在使用上传控件上传文件信息的时候,一定要注意在form 标签的属性中添加“enctype="multipart/form-data"”代码,否则使用Request的Files属性将获取不到请求中的文件信息。
⑹当同时上传多个文件的时候,要注意事务的使用,以确保多个文件同时上传成功。否则用户不知道哪些文件上传成功,哪些失败,将造成使用上的不便。