大文件下载问题(解决安全,速度,断点续传,多线程下载,服务器高占有问题)

时间:2024-02-23 16:56:29
namespace DownFile
{
    
using System;
    
using System.Web;
    
using System.Net;
    
using System.IO;
    
using System.Text;
    
using System.Data;
    
using System.Data.SqlClient;

    
/*
     * 1.支持断点续传
     * 2.支持多线程下载
     * 3.解决速度问题
     * 4.文件播放解决拖动问题
     * 5.解决服务器资源高占有问题
     
*/
 

    
/// <summary>
    
/// DownFile 的摘要说明。
    
/// </summary>

    public class GetFile : System.Web.UI.Page
    
{
        
/* 
         * 支持断点续传
         * 解决速度问题
         * 支持大文件
         
*/
 
        
private string file ;
        
private string fileHead ;
        
private string sqlConnString ;
        
public string SqlConnString{
            
get{
                
if(sqlConnString==null)
                    sqlConnString 
=  System.Configuration.ConfigurationSettings.AppSettings["SqlConnString"];
                
return sqlConnString;
            }
            
        }


        
private string headFilePath;

        
public string HeadFilePath{
            
get{
                
if(headFilePath==null)
                    headFilePath 
=  System.Configuration.ConfigurationSettings.AppSettings["HeadFilePath"];
                
return headFilePath;
            }
            
        }



        
private void Login(){
            
//    从IChannal中捕获UserName 和 PassWord;
            
//    <add key="LoginUrl" value="Http://192.168.2.89/yu.php?Require=name=111@111.com&pass=111111" />
            string loginUrl = System.Configuration.ConfigurationSettings.AppSettings["LoginUrl"];
            
//"asdfffgsdf-=-sd09-593%@#42593049523-4"
            
//loginUrl += "name=111@111.com&pass=111111";
            string userInfo = Request.Headers["UserInfo"];
            
if(userInfo == null){
                
// 用户没有登陆验证
                
// Response.Write("用户没有登陆!");
                Response.StatusDescription = "用户没有登陆!";
                ResponseEnd(
407);
            }

            loginUrl 
+= userInfo;
            
//利用 WebClient 取得远程登陆校验得结果
            WebClient wc = new WebClient();
            wc.Credentials 
= CredentialCache.DefaultCredentials;
            
try{
                
byte[] loginData = wc.DownloadData(loginUrl);
                
string loginResult = Encoding.Default.GetString(loginData);
                
//1 登陆成功 0 登陆失败 2 没有该用户名
                if(System.Convert.ToInt32(loginResult) == 1)
                    
return;
                
else if(System.Convert.ToInt32(loginResult) == 0){
                    
//用户登陆校验失败
                    
//Response.Write("用户登陆校验失败!");
                    Response.StatusDescription = "登陆失败!";
                    ResponseEnd(
418);
                }

                
else//(System.Convert.ToInt32(loginResult) == 2){
                    
//Response.Write("不存在改用户名!");
                    Response.StatusDescription = "登陆失败!";
                    ResponseEnd(
419);
                }

            }

            
catch(Exception){
                
//不能访问Url登陆,登陆结果异常,登陆失败
                Response.StatusDescription = "不能访问Url登陆,登陆结果异常,登陆失败!";
                ResponseEnd(
420);
            }

        }

        
private void Permit(){
            
string cGuid = Request.QueryString["cguid"];
            
string fileName = Request.QueryString["f"];
            
if(cGuid == null || fileName == null){
                Response.StatusDescription 
= "参数错误.";
                ResponseEnd(
412);
            }

            SqlConnection sqlConn 
= new SqlConnection(SqlConnString);
            sqlConn.Open();
            
//校验下载次数
            string sql ;
            SqlCommand sqlCmd 
= new SqlCommand();
            sqlCmd.CommandType 
= CommandType.Text;
            sqlCmd.Connection 
= sqlConn;
            sql 
= @"Select Retailers.RetailerUid From Retailers Inner Join LimitDownLoad on LimitDownLoad.RetailerId = Retailers.Id
                    Where LimitDownLoad.RetailerId = (Select RetailerId From LimitDownLoad Where ContentGuid=@ContentGuid)
                    Group By Retailers.LimitDown,Retailers.RetailerUid
                    Having Sum(LimitDownLoad.DownCount)
<Retailers.LimitDown";
            sqlCmd.CommandText = sql;
            sqlCmd.Parameters.Add(
"@ContentGuid",SqlDbType.VarChar,36);
            sqlCmd.Parameters[
"@ContentGuid"].Value = cGuid;
            Object objDown 
= sqlCmd.ExecuteScalar();
            
if(objDown == null){
                
//校验失败,或没有经过下载预置(LimitDownLoad)
                Response.StatusDescription = "校验失败,或没有经过下载预置(LimitDownLoad).";
                sqlConn.Close();
                ResponseEnd(
402);
            }

            
string rGuid = objDown.ToString();
            
//查找文件
            sql = @"Select OutputFile+\'\\'+FileName As FileName From WaitPack Inner join LimitDownLoad On LimitDownLoad.ContentId=WaitPack.ContentId Where IsPacking=3 And LimitDownLoad.ContentGuid=@ContentGuid And FileName=@FileName";
            sqlCmd.CommandText 
= sql;
            sqlCmd.Parameters.Clear();
            sqlCmd.Parameters.Add(
"@ContentGuid",SqlDbType.VarChar,36);
            sqlCmd.Parameters[
"@ContentGuid"].Value = cGuid;
            sqlCmd.Parameters.Add(
"@FileName",SqlDbType.VarChar,100);
            sqlCmd.Parameters[
"@FileName"].Value = fileName;
            Object objFile 
= sqlCmd.ExecuteScalar();
            
if(objFile == null){
                
//数据库中不存在改打包文件,即 该文件没有被打包
                Response.StatusDescription = "数据库中不存在改打包文件.";
                sqlConn.Close();
                ResponseEnd(
404);
            }

            
//从数据库中取得文件的绝对物理路径
            file = objFile.ToString();
            
//处理file中 \\192.168.2.3\\001.wmv => \\192.168.2.3\001.wmv 两者相等的
            file = file.Replace(@"\\",@"\");
            file 
= @"\"+file;
            
//--------end
            fileHead = HeadFilePath+rGuid+@"\"+GetHeadFile(file);
            fileHead 
= fileHead.Replace(@"\\",@"\");
            
//fileHead = @"\"+fileHead;
            if(!File.Exists(file) || !File.Exists(fileHead)){
                
//文件或头文件不存在!
                Response.StatusDescription = "文件或头文件不存在!";//或者是没有访问文件的权限
                sqlConn.Close();
                ResponseEnd(
404);
            }

            
//更新下载次数
            /*
             * 这部分 IChannal 那边下载完后处理
            sql = "Update LimitDownLoad Set DownCount=DownCount+1 Where ContentGuid=@ContentGuid";
            sqlCmd.CommandText = sql;
            sqlCmd.Parameters.Clear();
            sqlCmd.Parameters.Add("@ContentGuid",SqlDbType.VarChar,36);
            sqlCmd.Parameters["@ContentGuid"].Value = cGuid;
            sqlCmd.ExecuteNonQuery();
            
*/

            sqlConn.Close();
        }


        
private void ResponseEnd(int statusCode){
            Response.StatusCode 
= statusCode;
            Response.End();
        }
 

        
private string GetHeadFile(string file){
            
string f1 =    Path.GetFileNameWithoutExtension(file);
            
string f2 = Path.GetExtension(file);
            
return f1+"_h"+f2;
        }


        
private string GetMime(string fileName){
            
string fileExtent = Path.GetExtension(fileName);
            
//int index = fileName.LastIndexOf(\'.\');
            
//fileName.Substring(index,fileName.Length-index-1);
            string retMime;
            
switch(fileExtent){
                
case ".rm":
                    retMime 
= "application/vnd.rn-realmedia";
                    
break;
                
case ".mp3":
                    retMime 
= "application/mp3";
                    
break;
                
case ".asf":
                    retMime 
= "video/x-ms-asf";
                    
break;
                
case ".avi":
                    retMime 
= "video/avi";
                    
break;
                
case ".wav":
                    retMime 
= "audio/wav";
                    
break;
                
case ".wmv":
                    retMime 
= "audio/wmv";
                    
break;
                
case ".mpg":
                    retMime 
= "video/mpeg";
                    
break;
                
case ".mpeg":
                    retMime 
= "video/mpeg";
                    
break;
                
default:
                    retMime 
= "application/octet-stream";
                    
break;
            }

            
return retMime;
        }



        
protected override void OnInit(EventArgs e)
        
{
            
//登陆校验
            Login();
            
//过滤限制条件
            Permit();
            
//file = @"C:\xx.wmv";
            
//fileHead = @"C:\xx_h.wmv";
            
//文件以共享方式打开
            FileStream fs = new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.Read);
            Response.Clear();
            Response.ContentType 
= GetMime(file);
            
//Range: bytes=449794- || bytes=449794-588888
            FileStream fsHead = new FileStream(fileHead,FileMode.Open,FileAccess.Read,FileShare.Read);
            
byte[] buff = new byte[209152] ;
            
//文件长度,每个不同offset请求时,抛出给客户端的文件大小随之变化
            long length = fs.Length-10240+fsHead.Length;
            
//得到客户端请求文件定位位置
            string Range = Request.Headers["Range"];
            
//默认起始位置是文件开头,结尾位置是文件结尾
            
//去掉文件的头10K 大小!
            long offset=0,endset = fs.Length-10240+fsHead.Length;
            
int len; //实际读的文件的字节数
            Response.AppendHeader("Content-Disposition","attachment;filename=" +Path.GetFileName(file));
            Response.Buffer 
= true;
            Response.StatusCode 
= 200;
            
if(Range != null && Range != ""){
                Response.StatusCode 
= 206;
                
int index = Range.IndexOf(\'=\');
                Range 
= Range.Substring(index+1,Range.Length-index-1);
                index 
= Range.IndexOf(\'-\');
                
//取得文件的开始和结尾的文件位置
                offset = System.Convert.ToInt32(Range.Substring(0,index));
                
//if(index < Range.Length-1)
                    
//endset =  System.Convert.ToInt32(Range.Substring(index+1,Range.Length-index-1));
                length = endset - offset ;
                
//文件定位
                
//fs.Seek(offset,SeekOrigin.Begin);
            }

            Response.AppendHeader(
"Content-Length",length.ToString());
            
//仅仅定位到头文件中!
            
//抛出头文件中的类容
            if(offset < fsHead.Length) {
                
//头最大为10K左右 默认读取设置100k
                fsHead.Seek(offset,SeekOrigin.Begin);
                len 
= fsHead.Read(buff,0,102400);
                Response.OutputStream.Write(buff,
0,len);
                Response.Flush();
                fs.Seek(
10240,SeekOrigin.Begin);
            }

            
else{
                
//设置默认定位
                fs.Seek(offset-fsHead.Length+10240,SeekOrigin.Begin);
            }

            
//每次读取1M的类容
            len = fs.Read(buff,0,209152);//104576 2M
            while(len > 0 ) {
                
if (Response.IsClientConnected) {
                    Response.OutputStream.Write(buff,
0,len);
                    Response.Flush();
                    len 
= fs.Read(buff,0,209152);//1048576
                }

                
else {
                    len 
= 0;
                }

            }

            fs.Close();
            fsHead.Close();
            Response.End();
        }

        
    }

}