上传大文件
首先,为了处理大文件需要配置应用程序。有两个配置项影响着向服务器端提交大文件:
httpRuntime maxRequestLength
httpRuntime requestLengthDiskThreshold
maxRequestLength配置项指定提交的表单能被服务器接受的最大值。默认情况下,不能提交数据>4MB的表单。尝试提交内容过多的表单会得到一个异常。如果需要上传超过4MB的文件,那么需要配置该配置项。
requestLengthDiskThreshold配置项决定如何把上传表单缓存在文件系统。在老版本ASP.NET中,上传一个大文件可能会对服务器端造成一些严重影响,这是因为整个文件被上传到服务器端的内存中。例如,上传一个10MB的视频文件,就消耗了10MB的服务器端内存。
ASP.NET 3.5可以将大文件缓存在文件系统中。当文件的大小超过requestLengthDiskThreshold后,文件的余下部分将被缓存在文件系统中。
默认情况下,ASP.NET配置为把超过80KB的提交数据缓存到文件缓存器中。如果不喜欢这个配置,可以修改requestLengthDiskThreshold来这只新的阈值。(requestLengthDiskThreshold必须要小于maxRequestLength)。
下面的配置文件设置为可以上传不超过10MB的文件,并把缓存阈值改成100KB。
1 <configuration>
2 <system.web>
3 <httpRuntime
4 maxRequestLength="10240"
5 requestLengthDiskThreshold="100" />
6 </system.web>
7 </configuration>
8
处理大文件时,必须谨慎对待从数据存储区存储和获取文件时处理文件的方式。例如,从数据库表中获取文件时,不应把整个文件载入内存中。
下面的页面演示了如何高效的把一个大文件存储到数据库表中。
页面文件FileUploadLarge.aspx如下:
FileUploadLarge
1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="FileUploadLarge.aspx.cs" Inherits="Chapter4_FileUploadLarge" %>
2
3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
5 <html xmlns="http://www.w3.org/1999/xhtml">
6 <head runat="server">
7 <title>上传大文件</title>
8 <style type="text/css">
9 .fileList li
10 {
11 margin-bottom:5px;
12 }
13 </style>
14 </head>
15 <body>
16 <form id="form1" runat="server">
17 <div>
18 <asp:Label
19 ID="lblFile"
20 runat="server"
21 Text="Word Document:">
22 </asp:Label>
23
24 <asp:FileUpload
25 ID="upFile"
26 runat="server" />
27
28 <asp:Button
29 ID="btnAdd"
30 runat="server"
31 Text="Add Document"
32 onclick="btnAdd_Click" />
33
34 <hr />
35
36 <asp:Repeater
37 ID="rptFiles"
38 runat="server"
39 DataSourceID="srcFiles">
40 <HeaderTemplate>
41 <ul class="fileList">
42 </HeaderTemplate>
43 <ItemTemplate>
44 <li>
45 <asp:HyperLink
46 ID="lnkFile"
47 Text='<%# Eval("FileName") %>' NavigateUrl='<%# Eval("Id","~/Chapter4/FileHandlerLarge.ashx?id={0}") %>'
48 runat="server">
49 </asp:HyperLink>
50 </li>
51 </ItemTemplate>
52 <FooterTemplate>
53 </ul>
54 </FooterTemplate>
55 </asp:Repeater>
56
57 <asp:SqlDataSource
58 ID="srcFiles"
59 runat="server"
60 ConnectionString=
61 "<%$ConnectionStrings:myDBConnectionString %>"
62 SelectCommand=
63 "SELECT [Id], [FileName] FROM [Files]">
64 </asp:SqlDataSource>
65 </div>
66 </form>
67 </body>
68 </html>
69
后台代码FileUploadLarge.aspx.cs代码如下:
FileUploadLarge.aspx.cs
1 using System;
2 using System.Data;
3 using System.Data.Sql;
4 using System.Data.SqlClient;
5 using System.IO;
6
7 public partial class Chapter4_FileUploadLarge : System.Web.UI.Page
8 {
9 //数据库连接字符串
10 string conString =
11 System.Configuration.ConfigurationManager.ConnectionStrings["myDBConnectionString"].ConnectionString;
12
13 protected void Page_Load(object sender, EventArgs e)
14 {
15
16 }
17
18 protected void btnAdd_Click(object sender, EventArgs e)
19 {
20 if (upFile.HasFile)
21 {
22 if (CheckFileType(upFile.FileName))
23 {
24 AddFile(upFile.FileName, upFile.FileContent);
25 rptFiles.DataBind();
26 }
27 }
28 }
29
30 bool CheckFileType(string fileName)
31 {
32 return Path.GetExtension(fileName).ToLower() == ".doc";
33 }
34
35 void AddFile(string fileName, Stream upload)
36 {
37 SqlConnection con = new SqlConnection(conString);
38 SqlCommand cmd = new SqlCommand("INSERT INTO Files(FileName) VALUES (@FileName);" +
39 "SELECT @IDENTITY=SCOPE_IDENTITY()",con);
40 cmd.Parameters.AddWithValue("@FileName", fileName);
41 SqlParameter idParm = cmd.Parameters.Add("@IDENTITY", SqlDbType.Int);
42 idParm.Direction = ParameterDirection.Output;
43 using (con)
44 {
45 con.Open();
46 cmd.ExecuteNonQuery();
47 int newFileId = (int)idParm.Value;
48 StoreFile(newFileId, upload, con);
49 }
50 }
51
52 void StoreFile(int fileId,Stream upload,SqlConnection connection)
53 {
54 //每次存储的大小,微软建议在使用 .WRITE子句更新数据表时,应把缓存设为8040的倍数
55 int bufferLen=8040;
56 //进行2进制编码
57 BinaryReader br = new BinaryReader(upload);
58 //先读入首个8040字节
59 byte[] chunk = br.ReadBytes(bufferLen);
60
61 //将首个8040字节写入数据库中,因为Write子句不允许在空值上进行操作
62 //所以要先插入第一个chunk, 然后用WRITE子句更新
63 SqlCommand cmd = new SqlCommand("UPDATE Files SET FileBytes=@Buffer WHERE Id=@FileId", connection);
64 cmd.Parameters.AddWithValue("@FileId",fileId);
65 cmd.Parameters.Add("@Buffer", SqlDbType.VarBinary, bufferLen).Value = chunk;
66 cmd.ExecuteNonQuery();
67
68 //用write子句更新
69 SqlCommand cmdAppend =
70 new SqlCommand("UPDATE Files SET FileBytes .WRITE(@Buffer,null,0) WHERE Id=@FileId",connection);
71 cmdAppend.Parameters.AddWithValue("@FileId", fileId);
72 cmdAppend.Parameters.Add("@Buffer", SqlDbType.VarBinary, bufferLen);
73 chunk = br.ReadBytes(bufferLen);
74
75 //将数据分批次存入数据库,直至chunk为0
76 while (chunk.Length > 0)
77 {
78 cmdAppend.Parameters["@Buffer"].Value = chunk;
79 cmdAppend.ExecuteNonQuery();
80 chunk = br.ReadBytes(bufferLen);
81 }
82
83 //关闭读取器
84 br.Close();
85 }
86 }
在上述代码中,首先调用了AddFile() 方法。该方法在包含文件名字段的数据库表Files中新建一行。然后调用StoreFile()方法,这个方法把上传文件的实际字节加到数据库中。上传文件的内容被分为8040字节大小块。
*微软建议在使用.WRITE子句更新数据库时,应把缓存大小设成8040的倍数。
上述代码不会再内存中装载整个上传文件。上传文件以8040字节为大小的块从文件系统中取出,并一块一块的存入SQL Server中。
点击文件名,将执行FileUploadLarge.ashx HTTP处理程序。该处理程序从数据库中获取选中的文件并把它发送到浏览器。
FileUploadLarge.ashx
1 <%@ WebHandler Language="C#" Class="FileHandlerLarge" %>
2
3 using System;
4 using System.Web;
5 using System.Data;
6 using System.Data.SqlClient;
7
8 public class FileHandlerLarge : IHttpHandler {
9 //数据库连接字符串
10 string conString =
11 System.Configuration.ConfigurationManager.ConnectionStrings["myDBConnectionString"].ConnectionString;
12
13 public void ProcessRequest (HttpContext context) {
14 //由于禁用了缓存,处理程序输出就不会在传输到浏览器前缓在服务器端的内存中
15 context.Response.Buffer = false;
16 context.Response.ContentType = "application/msword";
17
18 SqlConnection con = new SqlConnection(conString);
19 SqlCommand cmd = new SqlCommand("SELECT FileBytes FROM Files WHERE Id=@Id",con);
20 cmd.Parameters.AddWithValue("@Id", context.Request["Id"]);
21 using (con)
22 {
23 con.Open();
24 //使DataReader将数据作为流来加载
25 SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
26 if (reader.Read())
27 {
28 //缓冲区大小
29 int bufferSize = 8040;
30 //缓冲区
31 byte[] chunk = new byte[bufferSize];
32 //每次的读取的字节数
33 long retCount;
34 long startIndex = 0;
35
36 //获取第一个8040字节
37 retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);
38
39 //循环获取剩余的数据
40 while (retCount == bufferSize)
41 {
42 context.Response.BinaryWrite(chunk);
43 startIndex += bufferSize;
44 retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);
45 }
46
47 //最后的不足8040字节的数据
48 byte[] actualChunk = new byte[retCount - 1];
49 Buffer.BlockCopy(chunk, 0, actualChunk, 0, (int)retCount - 1);
50 context.Response.BinaryWrite(actualChunk);
51 }
52 }
53 }
54
55 public bool IsReusable {
56 get {
57 return false;
58 }
59 }
60 }
上述的HTTP处理程序使用SqlDataReader从数据库中获取文件。注意,SqlDataReader使用CommandBehavior.SequentialAccess参数获取数据,这个参数使SqlDataReader以流的方式加载数据。数据库中的字段以8040字节大小的块存入内存中,这些内容块以Response.BinaryWrite()方法写入浏览器中。
注意这个处理程序禁用了响应缓存,Response.Buffer属性的值被设成False。由于禁用了缓存,处理程序输出就不会在传输到浏览器前缓存在服务器端的内存中。
*本节描述的处理大文件的方法只能工作在SQL Server2005上,使用SQL Server的早期版本时,需要使用TEXTPTR()函数而不是.WRITE子语句。
posted on 2010-12-13 14:58 DilibraLee 阅读(580) 评论(0) 收藏 举报

浙公网安备 33010602011771号