在开发Web应用程序时,经常需要为用户提供一个直观的反馈,尤其是在处理耗时较长的操作,如文件上传时。本文将介绍如何在ASP.NET2.0和.NET Framework2.0中创建一个显示上传进度的进度条。这个进度条会随着文件上传的进度实时更新,而不会触发页面回传。
最初,尝试在服务器端计算进度,并使用JavaScript定时器函数通过XMLHttpRequest对象从客户端更新进度条。但这种方法效果不佳,因为进度条要么显示0%,要么直接跳到100%,无法显示连续的进度。通过使用Mozilla Firebug追踪XMLHttpRequest对象的请求模式,发现XMLHttpRequest请求会在服务器端事件处理程序完成执行(即上传操作)后排队。因此,要么在服务器端上传开始之前就触发请求,要么在上传完成后触发,导致进度条无法正确显示进度。
在此,要感谢Munur Shaik先生提供的创意,在此基础上创建了进度条。同时,也要感谢架构师Anil Sistla对支持,帮助完成了解决方案。希望这篇文章能为那些寻找类似解决方案的人节省大量时间。
下面展示了源代码结构。UploadProgressBar.ascx文件是一个用户控件,显示单个文件上传控件以及添加/移除按钮。添加按钮用于添加多个文件上传控件,而移除按钮则逐个清除文件上传控件。
FileUploadIframe.aspx包含一个简单的HTML IFrame标签,用于加载控件页面(FileUpload.aspx)。在这里,使用IFrame的原因是,放置多文件上传的主页面在按下上传按钮时不应该回传。
以下是创建进度条的代码:
public void CreateProgress(HttpFileCollection fileCollection)
{
StringBuilder sbProgress = new StringBuilder();
sbProgress.Append("<html><head><LINK REL=StyleSheet HREF='StyleSheet1.css' TYPE='text/css' /></head> <body>");
sbProgress.Append("<script src='JScript1.js' type='text/javascript'></script><table id='mainTable' border='1'>");
for (int i = 0; i < fileCollection.Count; i++)
{
string strProgressBarId = "progressBar" + i;
string strPercentageId = "percentage" + i;
string fileName = fileCollection[i].FileName;
sbProgress.Append("<tr><td><p>" + fileName + "</p></td><td><div id='xxx' style='background-color:White; width: 100px;height:15px; border-color:Black;border-width:thin;border-style:solid'>");
sbProgress.Append("<div class='progressBar' id='" + strProgressBarId + "' ></div></div></td>");
sbProgress.Append("<td><p><div id='" + strPercentageId + "'></div></p></td></tr>");
}
sbProgress.Append("</table>");
for (int i = 0; i < fileCollection.Count; i++)
{
sbProgress.Append("<script type='text/javascript'> SetProgressBarProgressAmount(" + i + ",0 );</script>");
}
sbProgress.Append(PrepareTheGridHtml());
sbProgress.Append("</body><html>");
HttpContext.Current.Response.Write(sbProgress.ToString());
HttpContext.Current.Response.Flush();
}
上述CreateProgress()方法基本上将进度条HTML输出到输出流中,以创建一个空的进度条。在上面的代码中,SetProgressBarProgressAmount()是一个JavaScript方法,它接受两个参数:第一个参数是进度条的ID,这是在显示多个进度条时必需的;第二个参数是进度量。随着文件下载到服务器,这个进度条将根据下载的百分比设置,最后,当文件下载完成时,它将被设置为100%。在多文件上传的情况下,将显示多个进度条,每个文件一个,文件将一个接一个地上传到服务器,可以看到每个文件的进度量。
以下是将文件下载到服务器的代码:
public void DownloadTheFileToServer(HttpPostedFile file, int id)
{
Stream stream = null;
FileStream fs = null;
#region File Download Code goes here
try
{
string strFileName = System.IO.Path.GetFileName(file.FileName);
int contentLength = file.ContentLength;
stream = file.InputStream;
long totalUploadSize = stream.Length;
int bufferSize = 0;
if (totalUploadSize <= 1024)
{
bufferSize = 1024;
}
else if (bufferSize <= 4096)
{
bufferSize = 4096;
}
else if (bufferSize <= 8192)
{
bufferSize = 8192;
}
else
{
bufferSize = 16384;
}
byte[] b = new byte[1024];
int tripDownloadSize = 0;
long totalDownloadedSize = 0;
float Percentage = 0;
bool isNewFile = true;
string fileStoreLocation = ConfigurationManager.AppSettings["FileStoreLocation"];
fs = new FileStream(fileStoreLocation + strFileName, FileMode.Append);
while ((tripDownloadSize = stream.Read(b, 0, 1024)) > 0)
{
fs.Write(b, 0, tripDownloadSize);
totalDownloadedSize += tripDownloadSize;
Percentage = (int)(totalDownloadedSize * 100) / totalUploadSize;
setProgressBar(id, Percentage.ToString());
System.Threading.Thread.Sleep(100);
isNewFile = false;
}
}
catch (Exception objException)
{
throw objException;
}
finally
{
if (stream != null)
{
stream.Close();
stream.Dispose();
}
if (fs != null)
{
fs.Close();
fs.Dispose();
}
}
}
在上述方法中,根据文件大小,编写了逻辑来决定流的缓冲区大小,但开发者可以根据自己的需求设置缓冲区大小(字节数组大小)。本文主要关注如何在文件下载过程中创建/更新进度。在上述代码中,在while循环内,每次计算总下载大小,并使用公式:总下载大小 / 总文件大小 * 100来确定下载的百分比。使用setProgressBar(id, Percentage)方法,可以为特定ID的进度条设置进度。
以下是SetProgressBar方法的代码:
public static void setProgressBar(int id, string progressAmount)
{
StringBuilder sb = new StringBuilder();
sb.Append("<body><script type='text/javascript'>SetProgressBarProgressAmount(" + id + ",'" + progressAmount + "'); </script></body>");
HttpContext.Current.Response.Write(sb.ToString());
HttpContext.Current.Response.Flush();
}