C# 大文件分塊下載

Responsehtml

Http 協議中有專門的指令來告知瀏覽器, 本次響應的是一個須要下載的文件. 格式以下:
Content-Disposition: attachment;filename=filename.ext
以上指令即標記這次響應流是附件,且附件文件名爲 filename.ext。瀏覽器

 

文件下載方式:緩存

protected void Page_Load(object sender, EventArgs e)
{
    Response.WriteFile("Tree.jpg");
    Response.Flush();
    Response.Close();
}

protected void Page_Load(object sender, EventArgs e)
{
    Response.BinaryWrite(File.ReadAllBytes(Server.MapPath("Tree.jpg")));
    Response.Flush();
    Response.Close();
}

protected void Page_Load(object sender, EventArgs e)
{
    int chunkSize = 64;
    byte[] buffer = new byte[chunkSize];
    int offset = 0;
    int read = 0;
    using (FileStream fs = File.Open(Server.MapPath("Tree.jpg"), FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        while ((read = fs.Read(buffer, offset, chunkSize)) > 0)
        {
            Response.OutputStream.Write(buffer, 0, read);
            Response.Flush();
        }
    }

    Response.Close();
}

 

 

1.分塊下載服務器物理路徑下的文件服務器

/// <summary>
        /// 使用OutputStream.Write分塊下載文件  
        /// </summary>
        /// <param name="filePath"></param>
        public void WriteFileBlock(string filePath)
        {
            filePath = Server.MapPath(filePath);
            if (!File.Exists(filePath))
            {
                return;
            }
            FileInfo info = new FileInfo(filePath);
            //指定塊大小   
            long chunkSize = 4096;
            //創建一個4K的緩衝區   
            byte[] buffer = new byte[chunkSize];
            //剩餘的字節數   
            long dataToRead = 0;
            FileStream stream = null;
            try
            {
                //打開文件   
                stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                
                dataToRead = stream.Length;

                //添加Http頭   
                HttpContext.Current.Response.ContentType = "application/octet-stream";
                HttpContext.Current.Response.AddHeader("Content-Disposition", "attachement;filename=" + Server.UrlEncode(info.FullName));
                HttpContext.Current.Response.AddHeader("Content-Length", dataToRead.ToString());

                while (dataToRead > 0)
                {
                    if (HttpContext.Current.Response.IsClientConnected)
                    {
                        int length = stream.Read(buffer, 0, Convert.ToInt32(chunkSize));
                        HttpContext.Current.Response.OutputStream.Write(buffer, 0, length);
                        HttpContext.Current.Response.Flush();
                        HttpContext.Current.Response.Clear();
                        dataToRead -= length;
                    }
                    else
                    {
                        //防止client失去鏈接   
                        dataToRead = -1;
                    }
                }
            }
            catch (Exception ex)
            {
                HttpContext.Current.Response.Write("Error:" + ex.Message);
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
                HttpContext.Current.Response.Close();
            }

        }

2.使用WebClient下載http文件到客戶端。分塊下載,防止大文件下載阻塞瀏覽器。app

Response.Flush和Response.BufferOutputide

Response.Flush方法用來將緩衝區的數據當即輸出到瀏覽器當中。你能夠屢次調用Response.Flush 方法,當這樣使用時,瀏覽器將屢次接受數據,而不是僅接受一次數據。post

Response.BufferOutput是一個布爾值,指示是否緩衝輸出並在整個頁面在服務器端處理完畢後才發送緩衝區中的數據。true是其默認值。spa

服務器端是否緩存數據取決於Response.BufferOutput,當你將Response.BufferOutput的值設爲true時,數據會緩存到buffer中,並在頁面處理完畢後,將buffer中的內容一次性所有發到客戶端。若是爲false,則不緩衝數據,每執行一個response.write方法,數據就會當即發往客戶端,數據的傳送次數取決於你使用了多少個response.write方法,在這種狀況下,使用response.Flush方法是沒有意義的。只用當你將Response.BufferOutput屬性的值設爲true時,使用response.Flush方法纔有意義。這時服務器端會將調用response.Flush方法時以前的全部response.write方法的數據發往客戶端。code

只要將Response.BufferOutput的值設置爲true,必定會發送buffer裏的內容,只是遲早、次數的問題,這就取決於Response.Flush方法了。orm

至於它們的做用,在一個很大很大的網頁中,可使用Response.Flush方法將數據分批發往客戶端,這樣就可使瀏覽器先呈現一些html代碼,並逐步完整呈現。這樣可以使用戶減小等待時間。不過你要注意一下,發送的html代碼必須是閉合完整的,不然有的瀏覽器不會當即呈現html,而是等待接受完整的html才呈現。不然使用它就沒有效果了。

public void DownloadFile(string fileUri, string programId)
        {
            Stream stream = null;

            try
            {
                var fileName = programId + DateTime.Now.ToString("_HHmmssms") + ".ts";
                using (var client = new WebClient())
                {
                    client.Proxy = null;
                    stream = client.OpenRead(fileUri);
                    Response.Clear();
                    //Response.BufferOutput = false;
                    Response.ContentType = "video/mpeg";
                    //通知瀏覽器下載文件而不是打開 
                    Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
                    Int64 bytesTotal = Convert.ToInt64(client.ResponseHeaders["Content-Length"]);//獲取client響應頭文件長度
                    Response.AddHeader("Content-Length", bytesTotal.ToString());//設置文件大小
                    int bytesRead = 0;

                    long chunkSize = 4096;  //指定塊大小   
                    byte[] buffer = new byte[chunkSize];//創建一個4K的緩衝區 
                    while (Response.IsClientConnected && (bytesRead = stream.Read(buffer, 0, Convert.ToInt32(chunkSize))) > 0)
                    {
                        Response.OutputStream.Write(buffer, 0, bytesRead);
                        Response.Flush();//將緩衝的輸出數據發送到客戶端
                        Response.Clear();
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
                Response.Close();
            }
        }

 

3.文件斷點續傳

/// <summary>
        /// 文件斷點續傳下載
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="filePath"></param>
        /// <param name="speed"></param>
        /// <returns></returns>
        public static bool DownloadFile(HttpContext httpContext, string filePath, long speed)
        {
            bool ret = true;
            try
            {

                switch (httpContext.Request.HttpMethod.ToUpper())
                { //support Get and head method
                    case "GET":
                    case "HEAD":
                        break;
                    default:
                        httpContext.Response.StatusCode = 501;
                        return false;
                }
                if (!File.Exists(filePath))
                {
                    httpContext.Response.StatusCode = 404;
                    return false;
                }


                long startBytes = 0;
                int packSize = 1024 * 10; //read in block,every block 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);//the number of millisecond
                string lastUpdateTiemStr = File.GetLastWriteTimeUtc(filePath).ToString("r");
                string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + lastUpdateTiemStr;

                //validate whether the file is too large
                if (myFile.Length > Int32.MaxValue)
                {
                    httpContext.Response.StatusCode = 413;
                    return false;
                }

                if (httpContext.Request.Headers["If-Range"] != null)
                {

                    if (httpContext.Request.Headers["If-Range"].Replace("\"", "") != eTag)
                    {
                        httpContext.Response.StatusCode = 412;
                        return false;
                    }
                }

                try
                {
                    #region -------添加劇要響應頭、解析請求頭、相關驗證-------------------
                    httpContext.Response.Clear();
                    httpContext.Response.BufferOutput = false;
                    //httpContext.Response.AddHeader("Content-MD5", GetMD5Hash(myFile));
                    httpContext.Response.AddHeader("Accept-Ranges", "bytes");
                    httpContext.Response.AppendHeader("ETag", "\"" + eTag + "\"");
                    httpContext.Response.AppendHeader("Last-Modified", lastUpdateTiemStr);
                    httpContext.Response.ContentType = "application/octet-stream";
                    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;
                    if (httpContext.Request.Headers["Range"] != null)
                    {
                        httpContext.Response.StatusCode = 206;
                        string[] range = httpContext.Request.Headers["Range"].Split(new char[] { '=', '-' });
                        startBytes = Convert.ToInt64(range[1]);
                        if (startBytes < 0 || startBytes >= fileLength)
                        {
                            return false;
                        }
                    }
                    if (startBytes > 0)
                    {
                        // 若是是續傳請求,告訴客戶端本次的開始字節數,總長度,以便客戶端將續傳數據追加到startBytes位置後
                        httpContext.Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
                    }
                    #endregion

                    #region -------向客戶端發送數據塊-------------------
                    //send data 
                    br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
                    int maxCount = (int)Math.Ceiling((fileLength - startBytes + 0.0) / packSize);//download in block
                    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;
        }
相關文章
相關標籤/搜索