ASP.NET MVC5 批量下载文件并压缩

压缩方法:参考c#压缩文件
下载方法:ASP.NET文件下载各种方式比较:对性能的影响、对大文件的支持、对断点续传和多线程下载的支持
思路:先在服务器将要下载的文件压缩,成功之后调用下载的方法下载
一、获取到你要下载的文件路径集合。并判断路径是否存在。若含有路径不存在的,返回并提示。

 //要保存的文件路径
 List<string> filesOrDirectoriesPaths = new List<string>();
 //获取文件路径不存在
 List<LwChcekVo> NoneDirectoriesPaths = new List<LwChcekVo>();
 //获取要下载的文件路径
 for (int i = 0; i < listXwlw.Count; i++)
{
	 //判断文件存放路径是否为空
 	if (!string.IsNullOrEmpty(list[i].Wjlj))
     {
          //获取文件的相对物理路径
          string strUrl = Server.MapPath("~/");
          strUrl = strUrl + list[i].Wjlj;
          strUrl = strUrl.Replace("/","\\");
          //判断文件路径是否存在
          if (System.IO.File.Exists(strUrl))
           {
				filesOrDirectoriesPaths.Add(strUrl);
            }
            else
            {
                list[i].wjlj= strUrl;
          		 NoneDirectoriesPaths.Add(list[i]);
             }
       }
  }
 //文件不存在
if (NoneDirectoriesPaths.Count > 0)
 {
 	  string RestrMsg = "";
  	 foreach (var item in NoneDirectoriesPaths)
  	  {
 		 string str = item.wjlj.Replace("\\", "/");
 		 RestrMsg += "学生:" + item.Xm + "的论文未找到;文件路径为:" + str + "。";
  	   }
  	   RestrMsg = RestrMsg.Replace("\n", "").Replace(" ", "").Replace("\t", "").Replace("\r", "");
       return Content("<script>alert('" + RestrMsg + "请联系管理员!');history.go(-1);</script>");
 }

二、压缩前的处理并调用压缩方法压缩且返回压缩结果。压缩成功之后调用下载方法
压缩方法:ZipFileFromDirectory(要压缩的文件集合,压缩文件之后的保存路径,要用作替换的路径,压缩等级)

				   //判断是否存在保存临时压缩文件路径,不存在就创建
  				  if (!Directory.Exists(Server.MapPath("~/Document/TempThesis/")))
                    {
                        Directory.CreateDirectory(Server.MapPath("~/Document/TempThesis/"));
                    }

                    var strZipTopDirectoryPath = Server.MapPath("~/");
                    strZipTopDirectoryPath = strZipTopDirectoryPath + "Document\\Thesis\\";//用来将路径中的文件名替换掉,只保存文件名
                    const int intZipLevel = 9;//压缩级别 1-9
                    string DataTime = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
                    string filename = "下载-" + DataTime + ".zip";
                    string filePath = Server.MapPath("~/Document/TempThesis/" + filename);
                    if (filesOrDirectoriesPaths.Count > 0)
                    {
                    	//调用压缩文件的方法
                        string result = ZIPCompressUtil.ZipFileFromDirectory(filesOrDirectoriesPaths, filePath, strZipTopDirectoryPath, intZipLevel);
                        //处理压缩之后返回的信息
                        string[] results = result.Split('=');
						//压缩成功调用下载文件的方法
                        if (results[0].Trim() == "success")
                        {
                            long speed = 1024 * 1024 * 100;
                            //获取当前请求的HTTPContext对象
                            HttpContext http = System.Web.HttpContext.Current;
                            //调用下载的方法
                            bool ret = ZIPCompressUtil.DownloadFile(http, filePath, speed);
                            //下载成功
                            if (ret)
                            {
                            	//移除压缩的文件
                                System.IO.File.Delete(filePath);
                               
                            }
                            else
                            {
                            	//下载失败,移除压缩的文件
                                System.IO.File.Delete(filePath);
                                //return Content("<script>alert('下载失败,请重试!');history.go(-1);</script>");
                            }
                        }
                        else
                        {
                            string strMsg = results[1].Replace("“", "").Replace("”", "");
                            return Content("<script>alert('压缩失败,请重试!" + strMsg + "');history.go(-1);</script>");
                        }
                    }
                    else
                    {
                        return Content("<script>alert('没有要下载的文件!');history.go(-1);</script>");
                    }

三、压缩文件并保存文件的方法

	    /// <summary>
        /// 压缩选中的文件并保存到文件夹中
        /// </summary>
        /// <param name="ListPath">要压缩的文件集合</param>
        /// <param name="destinationPath">压缩文件之后的保存路径</param>
        /// <param name="strZipTopDirectoryPath">要用作替换的路径</param>
        /// <param name="compressLevel">压缩等级</param>
        /// <returns></returns>
        public static string ZipFileFromDirectory(List<string> ListPath, string destinationPath,  string strZipTopDirectoryPath, int compressLevel)
        {
            try
            {
                Crc32 crc = new Crc32();
                ZipOutputStream outPutStream = new ZipOutputStream(File.Create(destinationPath));
                outPutStream.SetLevel(compressLevel); // 0 - store only to 9 - means best compression  
                foreach (string file in ListPath)
                {
                    FileStream fileStream = File.OpenRead(file);//打开压缩文件  
                    byte[] buffer = new byte[fileStream.Length];
                    fileStream.Read(buffer, 0, buffer.Length);
                    string strFileName = file.Replace(strZipTopDirectoryPath, string.Empty);
                    if (strFileName.StartsWith(""))//检查是否文本开头
                    {
                        //下载的后保存的文件名,可更改
                        strFileName = strFileName.Substring(0);
                        //strFileName = "测试" + strFileName;
                    }
                    ZipEntry entry = new ZipEntry(strFileName);
                    entry.DateTime = DateTime.Now;

                    entry.Size = fileStream.Length;
                    fileStream.Close();
                    crc.Reset();
                    crc.Update(buffer);
                    entry.Crc = crc.Value;
                    outPutStream.PutNextEntry(entry);
                    outPutStream.Write(buffer, 0, buffer.Length);
                }
                outPutStream.Finish();
                outPutStream.Close();
                GC.Collect();
                return "success";
            }
            catch (Exception e)
            {
                GC.Collect();
                System.IO.File.Delete(destinationPath);
                return "fail=" + e.Message;
            }
        }

四、下载文件

	    /// <summary>
        /// 下载文件,支持大文件、续传、速度限制。支持续传的响应头Accept-Ranges、ETag,请求头Range 。
        /// Accept-Ranges:响应头,向客户端指明,此进程支持可恢复下载.实现后台智能传输服务(BITS),值为:bytes;
        /// ETag:响应头,用于对客户端的初始(200)响应,以及来自客户端的恢复请求,
        /// 必须为每个文件提供一个唯一的ETag值(可由文件名和文件最后被修改的日期组成),这使客户端软件能够验证它们已经下载的字节块是否仍然是最新的。
        /// Range:续传的起始位置,即已经下载到客户端的字节数,值如:bytes=1474560- 。
        /// 另外:UrlEncode编码后会把文件名中的空格转换中+(+转换为%2b),但是浏览器是不能理解加号为空格的,所以在浏览器下载得到的文件,空格就变成了加号;
        /// 解决办法:UrlEncode 之后, 将 "+" 替换成 "%20",因为浏览器将%20转换为空格
        /// </summary>
        /// <param name="httpContext">当前请求的HttpContext</param>
        /// <param name="filePath">下载文件的物理路径,含路径、文件名</param>
        /// <param name="speed">下载速度:每秒允许下载的字节数</param>
        /// <returns>true下载成功,false下载失败</returns>
        public static bool DownloadFile(HttpContext httpContext, string filePath, long speed)
        {
            httpContext.Response.Clear();
            bool ret = false;
            try
            {
                #region#region --验证:HttpMethod,请求的文件是否存在#region
                switch (httpContext.Request.HttpMethod.ToUpper())
                { //目前只支持GET和HEAD方法
                    case "GET":
                    case "HEAD":
                        break;
                    default:
                        httpContext.Response.StatusCode = 501;
                        return false;
                }
                if (!System.IO.File.Exists(filePath))
                {
                    httpContext.Response.StatusCode = 404;
                    return false;
                }
                #endregion
                #region 定义局部变量
                long startBytes = 0;
                long stopBytes = 0;
                int packSize = 1024 * 10; //分块读取,每块10K bytes
                string fileName = Path.GetFileName(filePath);
                FileStream myFile = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                BinaryReader br = new BinaryReader(myFile);
                long fileLength = myFile.Length;
                int sleep = (int)Math.Ceiling(1000.0 * packSize / speed);//毫秒数:读取下一数据块的时间间隔
                string lastUpdateTiemStr = System.IO.File.GetLastWriteTimeUtc(filePath).ToString("r");
                string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + lastUpdateTiemStr;//便于恢复下载时提取请求头;
                #endregion
                #region --验证:文件是否太大,是否是续传,且在上次被请求的日期之后是否被修改过
                if (myFile.Length > long.MaxValue)
                {//-------文件太大了-------
                    httpContext.Response.StatusCode = 413;//请求实体太大
                    return false;
                }
                if (httpContext.Request.Headers["If-Range"] != null)//对应响应头ETag:文件名+文件最后修改时间
                {
                    //----------上次被请求的日期之后被修改过--------------
                    if (httpContext.Request.Headers["If-Range"].Replace("\"", "") != eTag)
                    {//文件修改过
                        httpContext.Response.StatusCode = 412;//预处理失败
                        return false;
                    }
                }
                #endregion
                try
                {

                    #region -------添加重要响应头、解析请求头、相关验证
                    httpContext.Response.Clear();
                    if (httpContext.Request.Headers["Range"] != null)
                    {//------如果是续传请求,则获取续传的起始位置,即已经下载到客户端的字节数------
                        httpContext.Response.StatusCode = 206;//重要:续传必须,表示局部范围响应。初始下载时默认为200
                        string[] range = httpContext.Request.Headers["Range"].Split(new char[] { '=', '-' });//"bytes=1474560-"
                        startBytes = Convert.ToInt64(range[1]);//已经下载的字节数,即本次下载的开始位置  
                        if (startBytes < 0 || startBytes >= fileLength)
                        {//无效的起始位置
                            return false;
                        }
                        if (range.Length == 3)
                        {
                            stopBytes = Convert.ToInt64(range[2]);//结束下载的字节数,即本次下载的结束位置  
                            if (startBytes < 0 || startBytes >= fileLength)
                            {
                                return false;
                            }
                        }
                    }
                    httpContext.Response.Buffer = false;
                    string filePathMD = FormsAuthentication.HashPasswordForStoringInConfigFile(filePath, "MD5");
                    httpContext.Response.AddHeader("Content-MD5", filePathMD);//用于验证文件
                    httpContext.Response.AddHeader("Accept-Ranges", "bytes");//重要:续传必须
                    httpContext.Response.AppendHeader("ETag", "\"" + eTag + "\"");//重要:续传必须
                    httpContext.Response.AppendHeader("Last-Modified", lastUpdateTiemStr);//把最后修改日期写入响应                
                    httpContext.Response.ContentType = "application/octet-stream";//MIME类型:匹配任意文件类型
                    httpContext.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", "%20"));
                    httpContext.Response.AddHeader("Content-Length", (fileLength - startBytes).ToString());
                    httpContext.Response.AddHeader("Connection", "Keep-Alive");
                    httpContext.Response.ContentEncoding = Encoding.UTF8;
                    //将文件写入Http相应输出流,不在内存中缓存文件
                    httpContext.Response.TransmitFile(filePath);
                    //httpContext.Response.WriteFile();
                    if (startBytes > 0)
                    {//------如果是续传请求,告诉客户端本次的开始字节数,总长度,以便客户端将续传数据追加到startBytes位置后----------
                        httpContext.Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
                    }
                    #endregion
                    #region -------向客户端发送数据块-------------------
                    br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
                    int maxCount = (int)Math.Ceiling((fileLength - startBytes + 0.0) / packSize);//分块下载,剩余部分可分成的块数
                    for (int i = 0; i < maxCount && httpContext.Response.IsClientConnected; i++)
                    {//客户端中断连接,则暂停
                        httpContext.Response.BinaryWrite(br.ReadBytes(packSize));
                        httpContext.Response.Flush();
                        if (sleep > 1) Thread.Sleep(sleep);
                    }
                    #endregion
                }
                catch
                {
                    ret = false;
                }
                finally
                {
                    br.Close();
                    myFile.Close();
                }
            }
            catch
            {
                ret = false;
            }
            return ret;
        }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值