.net Mvc文件下载的功能,大文件下载完成之后修改数据库功能

简介: 我服务器上文件只能下载一次,下载了之后就不能下载了,大文件或网速不好时,可能服务端文件流发送完了,客户端还没下载完,导致下载失败, 那么最好的办法就是:在续传时不判断(If-Range,或If-Match 不为空时不判断,仍然发送对应的文件流)就行了,这样有一个漏洞,就是一个文件没下载完时,可以同时下载很多次,但是没办法,客户使用浏览器下载不好控制(如果记录用户开始下载的次数,逻辑上也不行—他可以下载到一半,然后时间不够不下载了,下次再重新下载,这在业务上也是允许他没下载完时重新下载的)。

我服务器上文件只能下载一次,下载了之后就不能下载了,大文件或网速不好时,可能服务端文件流发送完了,客户端还没下载完,导致下载失败,

那么最好的办法就是:在续传时不判断(If-Range,或If-Match 不为空时不判断,仍然发送对应的文件流)就行了,这样有一个漏洞,就是一个文件没下载完时,可以同时下载很多次,但是没办法,客户使用浏览器下载不好控制(如果记录用户开始下载的次数,逻辑上也不行—他可以下载到一半,然后时间不够不下载了,下次再重新下载,这在业务上也是允许他没下载完时重新下载的)。


        #region 下载文件处理-downfiledeal
        /// <summary>
        /// 下载文件处理
        /// </summary>
        /// <param name="ResourceID">资源id</param>
        /// <param name="UserID">用户id</param>
        /// <param name="downtype">下载类型:1-购买,2-今日免费,3-订阅用户</param>
        /// <param name="webtitle">提示页面titie</param>
        /// <param name="ResourceType">资源类型(暂时不考虑)</param>
        /// <returns></returns>
        private string downfiledeal(string ResourceID, string UserID, string downtype, string webtitle, string ResourceType = "")
        {
            TopicInformationBLL bll_tf = new TopicInformationBLL();
            TopicInformation tf = bll_tf.GetById(ResourceID);
            bll_tf.Dispose();
            string FilePath = "";
            FilePath = tf.TopicSourceFile.ToLower();
            if (FilePath.StartsWith("http://www.***.com"))//不是外网文件夹服务器路径
            {
                FilePath = FilePath.Replace("http://www.***.com", "");
            }
            if (!FilePath.StartsWith("/")) FilePath = "/" + FilePath;
            FilePath = Server.MapPath(FilePath);
            FileInfo DownloadFile = new FileInfo(FilePath);
            System.IO.Stream iStream = null;
            int downsize = 1024;//读取web.config中的配置
            try { downsize = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["downsize"]); }
            catch { downsize = 1024; }
            int buffersize = 1024 * downsize;//默认每次发送1K;
            byte[] buffer = new Byte[buffersize];// Buffer to read 10K bytes in chunk:
            int currentlength;// Length of each read to buffer:
            long dataToRead;// Total bytes to read:
            long dataSended;//已发送的字节
            long datalength;//文件字节总长度
            string filename = System.IO.Path.GetFileName(FilePath); // Identify the file name.
            try
            {
                System.Collections.Specialized.NameValueCollection hds = Request.Headers;
                long startBytes = 0;
                Response.Clear();
                Response.ClearHeaders();
                if (Request.Headers["Range"] != null)
                {//------如果是续传请求,则获取续传的起始位置,即已经下载到客户端的字节数------  
                    Response.StatusCode = 206;//重要:续传必须,表示局部范围响应。初始下载时默认为200  
                    string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });//"bytes=1474560-"  
                    startBytes = Convert.ToInt64(range[1]);//已经下载的字节数,即本次下载的开始位置    
                    if (startBytes < 0 || startBytes >= DownloadFile.Length)
                    {//无效的起始位置  
                        startBytes = 0;
                    }
                }
                iStream = new System.IO.FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);// Open the file.
                dataSended = 0;
                datalength =
                dataToRead = iStream.Length - startBytes;// Total bytes to read:
                Response.Buffer = true;
                string lastUpdateTiemStr = System.IO.File.GetLastWriteTimeUtc(FilePath).ToString("r");
                string eTag = HttpUtility.UrlEncode(filename, Encoding.UTF8) + lastUpdateTiemStr;
                Response.AppendHeader("ETag", "\"" + eTag + "\"");//重要:续传必须 
                if (startBytes > 0)
                {
                    if (Request.Headers["If-Range"] != null)//(IE,360)对应响应头ETag:文件名+文件最后修改时间  
                    {
                        //----------上次被请求的日期之后被修改过-------------- 
                        string If_Range = Request.Headers["If-Range"].Replace("\"", "");
                        if (If_Range != eTag)
                        {//文件修改过  
                            Response.StatusCode = 412;//预处理失败  
                            return "文件名验证失败";
                        }
                    }
                    else if (Request.Headers["If-Match"] != null)//(火狐)对应响应头ETag:文件名+文件最后修改时间  
                    {
                        //----------上次被请求的日期之后被修改过--------------  
                        string If_Match = Request.Headers["If-Match"].Replace("\"", "");
                        if (If_Match != eTag)
                        {//文件修改过 
                            Response.StatusCode = 412;//预处理失败  
                            return "文件名验证失败";
                        }
                    }
                    iStream.Seek(startBytes, SeekOrigin.Begin);
                    Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, DownloadFile.Length - 1, DownloadFile.Length));
                    Response.AddHeader("Accept-Ranges", "bytes");//重要:续传必须  
                }
                Response.AppendHeader("Content-Length", (DownloadFile.Length - startBytes).ToString());

                Response.ContentType = "application/octet-stream";
                Response.AddHeader("Connection", "Keep-Alive");
                Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
                while (dataToRead > 0)// Read the bytes.
                {
                    if (Response.IsClientConnected)// Verify that the client is connected.
                    {
                        currentlength = iStream.Read(buffer, 0, buffersize);// Read the data in buffer.
                        Response.OutputStream.Write(buffer, 0, currentlength); // Write the data to the current output stream.
                        //Response.BinaryWrite(buffer);
                        Response.Flush();// Flush the data to the HTML output.
                        //buffer = new Byte[10000];
                        dataToRead = dataToRead - currentlength;
                        dataSended = dataSended + currentlength;
                        Thread.Sleep(1000);//每秒钟发送一次
                    }
                    else
                    {
                        dataToRead = -1;//prevent infinite loop if user disconnects
                    }
                }
                if (dataToRead == 0 && dataSended>0)//发送、下载完成datalength == dataSended
                {
                    //UpdateOrderWhenDownloaded(ResourceID, UserID, downtype);//下载完成,修改数据库状态
                }
                return "";
            }
            catch (Exception ex)
            {
                CommonFun.WritetTxtLog("下载出错:" + ex.Message);
                return CommonFun.GetInformationHtml(webtitle, "下载出错 : " + ex.Message);
            }
            finally
            {

                if (iStream != null)
                {
                    iStream.Close();//Close the file.
                }
            }
        }
        #endregion

        #region 返回显示标题的网页提示文本
        /// <summary>
        /// 返回显示标题的网页提示文本
        /// </summary>
        /// <param name="title">网站title</param>
        /// <param name="body">显示的文字</param>
        /// <returns></returns>
        public static string GetInformationHtml(string title, string body)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<html>");
            sb.Append("<head>");
            sb.AppendFormat("<title>{0}</title>",title);
            sb.Append("<style type=\"text/css\">");
            sb.Append(".fixed_div { position:absolute; z-index:2008; bottom:0px; left:0px; width:100%;text-align:center;height:20px; border:0px solid #e5e5e5; background:#e5e5e5; }");
            sb.Append(".margin_auto{margin:-7px auto;width:980px;height:100%;overflow:hidden;}");
            sb.Append(".nounderline{text-decoration:none;}");
            sb.Append(".info{color:red;border:0px solid #e5e5e5; background:#e5e5e5;height:100%;}");
            sb.Append("</style>");
            sb.Append("</head>");
            sb.Append("<body>");
            sb.Append("<div class=\"margin_auto\">");
            sb.Append("<div class=\"info\">");
            sb.Append("<span style=\"font-style:italic;\">提示信息:</span><br/>");
            sb.Append(body);
            sb.Append("</div>");
            sb.Append("<div class=\"fixed_div\">Power By <a href=\"http://www.***.com\" target=\"_blank\" class=\"nounderline\">www.kinpan.com</a><div>");
            sb.Append("</div>");
            sb.Append("</body>");
            sb.Append("</html>");
            return sb.ToString() ;
        }
        #endregion


如果response.writefile,或mvc 中的返回 fileresult,谁知道它下载完成了,这很难控制,现在这样发送,发送完成之后就下载完成了,还能续传,比较精确。。


相关文章
|
1月前
|
SQL 关系型数据库 MySQL
阿里云RDS云数据库全解析:产品功能、收费标准与活动参考
与云服务器ECS一样,关系型数据库RDS也是很多用户上云必买的热门云产品之一,阿里云的云数据库RDS主要包含RDS MySQL、RDS SQL Server、RDS PostgreSQL、RDS MariaDB等几个关系型数据库,并且提供了容灾、备份、恢复、监控、迁移等方面的全套解决方案,帮助您解决数据库运维的烦恼。本文为大家介绍阿里云的云数据库 RDS主要产品及计费方式、收费标准以及活动等相关情况,以供参考。
|
4月前
|
SQL 存储 关系型数据库
MySQL功能模块探秘:数据库世界的奇妙之旅
]带你轻松愉快地探索MySQL 8.4.5的核心功能模块,从SQL引擎到存储引擎,从复制机制到插件系统,让你在欢声笑语中掌握数据库的精髓!
185 26
|
9月前
|
存储 NoSQL 关系型数据库
PolarDB开源数据库进阶课17 集成数据湖功能
本文介绍了如何在PolarDB数据库中接入pg_duckdb、pg_mooncake插件以支持数据湖功能, 可以读写对象存储的远程数据, 支持csv, parquet等格式, 支持delta等框架, 并显著提升OLAP性能。
622 2
|
10月前
|
Cloud Native 关系型数据库 分布式数据库
让PolarDB更了解您--PolarDB云原生数据库核心功能体验馆
让PolarDB更了解您——PolarDB云原生数据库核心功能体验馆,由阿里云数据库产品事业部负责人宋震分享。内容涵盖PolarDB技术布局、开源进展及体验馆三大部分。技术布局包括云计算加速数据库演进、数据处理需求带来的变革、软硬协同优化等;开源部分介绍了兼容MySQL和PostgreSQL的两款产品;体验馆则通过实际操作让用户直观感受Serverless、无感切换、SQL2Map等功能。
381 7
|
6月前
|
存储 缓存 自然语言处理
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
193 8
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
|
8月前
|
SQL Linux 数据库
【YashanDB知识库】崖山数据库Outline功能验证
本文来自YashanDB官网,主要测试了数据库优化器在不同场景下优先使用outline计划的功能。测试环境包括相同版本新增数据、绑定参数执行、单机主备架构以及数据库版本升级等场景。通过创建表、插入数据、收集统计信息和创建outline等步骤,验证了在各种情况下优化器均能优先采用存储的outline计划。测试结果表明,即使统计信息失效或数据库版本升级,outline功能依然稳定有效,确保查询计划的一致性和性能优化。详情可见[原文链接](https://www.yashandb.com/newsinfo/7488286.html?templateId=1718516)。
【YashanDB知识库】崖山数据库Outline功能验证
|
8月前
|
NoSQL 关系型数据库 MongoDB
Apifox与Apipost数据库连接功能详细对比,让接口管理更高效!
在现代软件开发中,数据库是应用运行的核心组件,接口管理工具则是连接和调试数据库的重要桥梁。本文对比了 Apifox 和 Apipost 两款工具的数据库连接功能。Apipost 支持全面的关系型与非关系型数据库(如 MySQL、Redis、MongoDB),功能强大且免费,适合复杂项目;而 Apifox 在关系型数据库支持上表现良好,但非关系型数据库(尤其是 Redis)功能有限且收费,更适合中小项目以关系型数据库为主的需求。根据项目需求选择合适的工具,可显著提升开发效率和稳定性。
|
8月前
|
数据库连接 PHP 数据库
【YashanDB知识库】PHP使用ODBC使用数据库绑定参数功能异常
【YashanDB知识库】PHP使用ODBC使用数据库绑定参数功能异常
|
8月前
|
PHP 数据库
【YashanDB知识库】PHP使用OCI接口使用数据库绑定参数功能异常
【YashanDB知识库】PHP使用OCI接口使用数据库绑定参数功能异常
|
8月前
|
存储 NoSQL 关系型数据库
Apifox与Apipost数据库连接功能详细对比,让接口管理更高效!
Apipost 更加全面:无论是关系型还是非关系型数据库,它都为开发者提供了一站式解决方案,非常适合数据库架构复杂的大型项目。相对来说,Apifox偏重关系型分析和管理:若项目主要需求在于管理关系型数据库,而对非关系型的依赖较小,Apifox倒是可以应付。
235 2