как возобновить загрузку файла в asp.net с помощью С# -> лучший способ (также и для больших файлов)

см. обработчик ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace FileExplorer
{
    /// <summary>
    /// Summary description for HandlerForMyFE
    /// </summary>
    public class HandlerForMyFE : IHttpHandler, System.Web.SessionState.IRequiresSessionState
    {

        private HttpContext _context;
        private HttpContext Context
        {
            get
            {
                return _context;
            }
            set
            {
                _context = value;
            }
        }

        public void ProcessRequest(HttpContext context)
        {
            Context = context;
            string filePath = context.Request.QueryString["Downloadpath"];
            filePath = context.Server.MapPath(filePath);

            if (filePath == null)
            {
                return;
            }

            System.IO.StreamReader streamReader = new System.IO.StreamReader(filePath);
            System.IO.BinaryReader br = new System.IO.BinaryReader(streamReader.BaseStream);

            byte[] bytes = new byte[streamReader.BaseStream.Length];

            br.Read(bytes, 0, (int)streamReader.BaseStream.Length);

            if (bytes == null)
            {
                return;
            }

            streamReader.Close();
            br.Close();
            string fileName = System.IO.Path.GetFileName(filePath);
            string MimeType = GetMimeType(fileName);
            string extension = System.IO.Path.GetExtension(filePath);
            char[] extension_ar = extension.ToCharArray();
            string extension_Without_dot = string.Empty;
            for (int i = 1; i < extension_ar.Length; i++)
            {
                extension_Without_dot += extension_ar[i];
            }

            //if (extension == ".jpg")
            //{ // Handle *.jpg and
            //    WriteFile(bytes, fileName, "image/jpeg jpeg jpg jpe", context.Response);
            //}
            //else if (extension == ".gif")
            //{// Handle *.gif
            //    WriteFile(bytes, fileName, "image/gif gif", context.Response);
            //}

            if (HttpContext.Current.Session["User_ID"] != null)
            {
                WriteFile(bytes, fileName, MimeType + " " + extension_Without_dot, context.Response);
            }
        }

        private void WriteFile(byte[] content, string fileName, string contentType, HttpResponse response)
        {
            response.Buffer = true;
            response.Clear();
            response.ContentType = contentType;

            response.AddHeader("content-disposition", "attachment; filename=" + fileName);

            response.BinaryWrite(content);
            response.Flush();
            response.End();
        }

        private string GetMimeType(string fileName)
        {
            string mimeType = "application/unknown";
            string ext = System.IO.Path.GetExtension(fileName).ToLower();
            Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
            if (regKey != null && regKey.GetValue("Content Type") != null)
                mimeType = regKey.GetValue("Content Type").ToString();
            return mimeType;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}  

я использую этот обработчик для загрузки моих файлов, не открывая их непосредственно в браузере -> (используя путь строки запроса)

как я могу сделать мои файлы возобновляемыми?

у меня нет такой опции в интернет-менеджере загрузок!


person SilverLight    schedule 30.10.2011    source источник
comment
Как браузер возобновит загрузку? Какой протокол он будет использовать? Вам нужно начать с протокола, а затем писать код от него.   -  person John Saunders    schedule 31.10.2011
comment
спасибо jhon за комментарий - не могли бы вы объяснить больше / я понятия не имею о протоколе!   -  person SilverLight    schedule 31.10.2011
comment
я не смог найти полную ссылку на эту проблему в потоке стека или даже в сети...   -  person SilverLight    schedule 31.10.2011
comment
поэтому я задаю этот вопрос здесь /   -  person SilverLight    schedule 31.10.2011
comment
Ответ в том, что вы не можете. Что заставляет вас думать, что браузеры могут выполнять загрузки, которые можно возобновить?   -  person John Saunders    schedule 31.10.2011
comment
я просто ищу способ сделать мои файлы доступными для менеджеров загрузок, таких как интернет-менеджер загрузок и т. д.   -  person SilverLight    schedule 31.10.2011
comment
Это будет зависеть от менеджера загрузки. Вам нужно будет выяснить, как они реализуют функцию возобновления. Я могу гарантировать вам, что вам нужно будет изучить протокол HTTP.   -  person John Saunders    schedule 31.10.2011
comment
Это будет зависеть от менеджера загрузки -› я так не думаю - вы имеете в виду, что мы должны написать несколько кодов для каждого менеджера загрузки?   -  person SilverLight    schedule 31.10.2011
comment
Возможно, вам придется это сделать. Не существует единого протокола менеджера загрузки. Не существует простого переключателя, который можно настроить для перезапуска загрузок.   -  person John Saunders    schedule 31.10.2011
comment
уважаемый Джон Сондерс / я нашел способ не писать платформы для каждого менеджера загрузок в отдельности /   -  person SilverLight    schedule 31.10.2011
comment
Расскажите, пожалуйста, какой способ вы нашли. Четко ответьте на свой вопрос, и я (например) проголосую за него.   -  person John Saunders    schedule 31.10.2011
comment
Пожалуйста, посмотрите мой ответ / это было именно то, что я хочу! плз проголосуйте за меня...   -  person SilverLight    schedule 01.11.2011
comment
что вы думаете об этом Джоне? (работает идеально)   -  person SilverLight    schedule 01.11.2011
comment
Джон, пожалуйста, объясните нам и проголосуйте за меня!   -  person SilverLight    schedule 01.11.2011


Ответы (4)


В соответствии с запросом, вот «очищенная» версия ответа:

public static bool DownloadFileMethod(HttpContext httpContext, string filePath, long speed)
{
    // Many changes: mostly declare variables near use
    // Extracted duplicate references to HttpContext.Response and .Request
    // also duplicate reference to .HttpMethod

    // Removed try/catch blocks which hid any problems
    var response = httpContext.Response;
    var request = httpContext.Request;
    var method = request.HttpMethod.ToUpper();
    if (method != "GET" &&
        method != "HEAD")
    {
        response.StatusCode = 501;
        return false;
    }

    if (!File.Exists(filePath))
    {
        response.StatusCode = 404;
        return false;
    }

    // Stream implements IDisposable so should be in a using block
    using (var myFile = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        var fileLength = myFile.Length;
        if (fileLength > Int32.MaxValue)
        {
            response.StatusCode = 413;
            return false;
        }

        var lastUpdateTiemStr = File.GetLastWriteTimeUtc(filePath).ToString("r");
        var fileName = Path.GetFileName(filePath);
        var fileNameUrlEncoded = HttpUtility.UrlEncode(fileName, Encoding.UTF8);
        var eTag = fileNameUrlEncoded + lastUpdateTiemStr;

        var ifRange = request.Headers["If-Range"];
        if (ifRange != null && ifRange.Replace("\"", "") != eTag)
        {
            response.StatusCode = 412;
            return false;
        }

        long startBytes = 0;

        // Just guessing, but I bet you want startBytes calculated before
        // using to calculate content-length
        var rangeHeader = request.Headers["Range"];
        if (rangeHeader != null)
        {
            response.StatusCode = 206;
            var range = rangeHeader.Split(new[] {'=', '-'});
            startBytes = Convert.ToInt64(range[1]);
            if (startBytes < 0 || startBytes >= fileLength)
            {
                // TODO: Find correct status code
                response.StatusCode = (int) HttpStatusCode.BadRequest;
                response.StatusDescription =
                    string.Format("Invalid start of range: {0}", startBytes);
                return false;
            }
        }

        response.Clear();
        response.Buffer = false;
        response.AddHeader("Content-MD5", GetMD5Hash(filePath));
        response.AddHeader("Accept-Ranges", "bytes");
        response.AppendHeader("ETag", string.Format("\"{0}\"", eTag));
        response.AppendHeader("Last-Modified", lastUpdateTiemStr);
        response.ContentType = "application/octet-stream";
        response.AddHeader("Content-Disposition", "attachment;filename=" +
                                                    fileNameUrlEncoded.Replace("+", "%20"));
        var remaining = fileLength - startBytes;
        response.AddHeader("Content-Length", remaining.ToString());
        response.AddHeader("Connection", "Keep-Alive");
        response.ContentEncoding = Encoding.UTF8;

        if (startBytes > 0)
        {
            response.AddHeader("Content-Range",
                                string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
        }

        // BinaryReader implements IDisposable so should be in a using block
        using (var br = new BinaryReader(myFile))
        {
            br.BaseStream.Seek(startBytes, SeekOrigin.Begin);

            const int packSize = 1024*10; //read in block,every block 10K bytes
            var maxCount = (int) Math.Ceiling((remaining + 0.0)/packSize); //download in block
            for (var i = 0; i < maxCount && response.IsClientConnected; i++)
            {
                response.BinaryWrite(br.ReadBytes(packSize));
                response.Flush();

                // HACK: Unexplained sleep
                var sleep = (int) Math.Ceiling(1000.0*packSize/speed); //the number of millisecond
                if (sleep > 1) Thread.Sleep(sleep);
            }
        }
    }
    return true;
}
person Community    schedule 01.11.2011
comment
Я думаю, что если кто-то попытается загрузить файл из Chrome, это не сработает, потому что заголовок ifRange будет недоступен, я пробовал это в Chrome. - person Mubashar; 23.03.2014
comment
Как реализовать это решение в ядре dotnet? - person A Programmer; 25.05.2018
comment
@AProgrammer DotNet Core 2 и выше уже поддерживает это из коробки. Вы можете выбрать PhysicalFileResult, FileStreamResult или VirtualFileResult. Например, добавьте это в контроллер: return new PhysicalFileResult("myfile.zip", MediaTypeNames.Application.Zip);. Для более ранних версий .Net Core вы можете использовать этот пакет NuGet: ZNetCS.AspNetCore.ResumingFileResults. - person Louis Somers; 07.09.2020

вот ответ!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.IO;
using System.Threading;
using System.Security.Cryptography;

namespace NiceFileExplorer.Classes
{
    public class DownloadFile
    {
        public static bool DownloadFileMethod(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;
                }
                //#endregion

                var fileInfo = new FileInfo(filePath);

                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;
                    }
                }
                //#endregion

                try
                {

                    httpContext.Response.Clear();
                    httpContext.Response.Buffer = false;
                    httpContext.Response.AddHeader("Content-MD5", GetMD5Hash(fileInfo));
                    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)
                    {
                        httpContext.Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
                    }
                    //#endregion

                    //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;
        }

        private static string GetMD5Hash(FileInfo file)
        {
            var stream = file.OpenRead();
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(stream);
            stream.Close();

            var sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }
            return sb.ToString();
        }

    }
}
person SilverLight    schedule 31.10.2011
comment
Я проголосовал, потому что думаю, что ваш код может содержать ответ, который может помочь другим. Однако у вашего кода есть серьезные проблемы, и я был очень близок к тому, чтобы понизить голосование. Среди прочего, вы не показываете, что ваш код будет использоваться; у вас есть пустые блоки catch, и вы неправильно обрабатываете объекты IDisposable, такие как FileStream и BinaryReader. - person John Saunders; 01.11.2011
comment
не могли бы вы завершить мой ответ и исправить проблему с IDisposable! (спасибо за голос) - другой голосующий против -› это несправедливо - person SilverLight; 01.11.2011

А вот официальная реализация, предоставленная MSDN:

http://code.msdn.microsoft.com/Implement-resume-in-aspnet-c1bbde36/view/SourceCode

Downloader.cs

using System;
using System.IO;
using System.Text;
using System.Web;

namespace CSASPNETResumeDownload
{
    public class Downloader
    {
        public static void DownloadFile(HttpContext httpContext, string filePath)
        {
            if (!IsFileExists(filePath))
            {
                httpContext.Response.StatusCode = 404;
                return;
            }

            FileInfo fileInfo = new FileInfo(filePath);

            if (fileInfo.Length > Int32.MaxValue)
            {
                httpContext.Response.StatusCode = 413;
                return;
            }

            // Get the response header information by the http request.
            HttpResponseHeader responseHeader = GetResponseHeader(httpContext.Request, fileInfo);

            if (responseHeader == null)
            {
                return;
            }

            FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

            try
            {
                SendDownloadFile(httpContext.Response, responseHeader, fileStream);
            }
            catch (HttpException ex)
            {
                httpContext.Response.StatusCode = ex.GetHttpCode();
            }
            finally
            {
                fileStream.Close();
            }
        }

        /// <summary>
        /// Check whether the file exists.
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        private static bool IsFileExists(string filePath) 
        {
            bool fileExists = false;

            if (!string.IsNullOrEmpty(filePath))
            {
                if (File.Exists(filePath))
                {
                    fileExists = true;
                }
            }

            return fileExists;
        }

        /// <summary>
        /// Get the response header by the http request.
        /// </summary>
        /// <param name="httpRequest"></param>
        /// <param name="fileInfo"></param>
        /// <returns></returns>
        private static HttpResponseHeader GetResponseHeader(HttpRequest httpRequest, FileInfo fileInfo)
        {
            if (httpRequest == null)
            {
                return null;
            }

            if (fileInfo == null)
            {
                return null;
            }

            long startPosition = 0;
            string contentRange = "";

            string fileName = fileInfo.Name;
            long fileLength = fileInfo.Length;
            string lastUpdateTimeStr = fileInfo.LastWriteTimeUtc.ToString();

            string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + " " + lastUpdateTimeStr;
            string contentDisposition = "attachment;filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", "%20");

            if (httpRequest.Headers["Range"] != null)
            {
                string[] range = httpRequest.Headers["Range"].Split(new char[] { '=', '-' });
                startPosition = Convert.ToInt64(range[1]);
                if (startPosition < 0 || startPosition >= fileLength)
                {
                    return null;
                }
            }

            if (httpRequest.Headers["If-Range"] != null)
            {
                if (httpRequest.Headers["If-Range"].Replace("\"", "") != eTag)
                {
                    startPosition = 0;
                }
            }

            string contentLength = (fileLength - startPosition).ToString();

            if (startPosition > 0)
            {
                contentRange = string.Format(" bytes {0}-{1}/{2}", startPosition, fileLength - 1, fileLength);
            }

            HttpResponseHeader responseHeader = new HttpResponseHeader();

            responseHeader.AcceptRanges = "bytes";
            responseHeader.Connection = "Keep-Alive";
            responseHeader.ContentDisposition = contentDisposition;
            responseHeader.ContentEncoding = Encoding.UTF8;
            responseHeader.ContentLength = contentLength;
            responseHeader.ContentRange = contentRange;
            responseHeader.ContentType = "application/octet-stream";
            responseHeader.Etag = eTag;
            responseHeader.LastModified = lastUpdateTimeStr;

            return responseHeader;
        }

        /// <summary>
        /// Send the download file to the client.
        /// </summary>
        /// <param name="httpResponse"></param>
        /// <param name="responseHeader"></param>
        /// <param name="fileStream"></param>
        private static void SendDownloadFile(HttpResponse httpResponse, HttpResponseHeader responseHeader, Stream fileStream)
        {
            if (httpResponse == null || responseHeader == null)
            {
                return;
            }

            if (!string.IsNullOrEmpty(responseHeader.ContentRange))
            {
                httpResponse.StatusCode = 206;

                // Set the start position of the reading files.
                string[] range = responseHeader.ContentRange.Split(new char[] { ' ','=', '-' });
                fileStream.Position = Convert.ToInt64(range[2]);
            }
            httpResponse.Clear();
            httpResponse.Buffer = false;
            httpResponse.AppendHeader("Accept-Ranges", responseHeader.AcceptRanges);
            httpResponse.AppendHeader("Connection", responseHeader.Connection);
            httpResponse.AppendHeader("Content-Disposition", responseHeader.ContentDisposition);
            httpResponse.ContentEncoding = responseHeader.ContentEncoding;
            httpResponse.AppendHeader("Content-Length", responseHeader.ContentLength);
            if (!string.IsNullOrEmpty(responseHeader.ContentRange))
            {
                httpResponse.AppendHeader("Content-Range", responseHeader.ContentRange);
            }
            httpResponse.ContentType = responseHeader.ContentType;
            httpResponse.AppendHeader("Etag", "\"" + responseHeader.Etag + "\"");
            httpResponse.AppendHeader("Last-Modified", responseHeader.LastModified);

            Byte[] buffer = new Byte[10240];
            long fileLength = Convert.ToInt64(responseHeader.ContentLength);

            // Send file to client.
            while (fileLength > 0)
            {
                if (httpResponse.IsClientConnected)
                {
                    int length = fileStream.Read(buffer, 0, 10240);

                    httpResponse.OutputStream.Write(buffer, 0, length);

                    httpResponse.Flush();

                    fileLength = fileLength - length;
                }
                else
                {
                    fileLength = -1;
                }
            }
        }
    }

    /// <summary>
    /// Respresent the HttpResponse header information.
    /// </summary>
    class HttpResponseHeader
    {
        public string AcceptRanges { get; set;}
        public string Connection { get; set; }
        public string ContentDisposition { get; set; }
        public Encoding ContentEncoding { get; set; }
        public string ContentLength { get; set; }
        public string ContentRange { get; set; }
        public string ContentType { get; set; }
        public string Etag { get; set; }
        public string LastModified { get; set; }
    }
}

СкачатьHttpHandler.ashx.cs

using System;
using System.Configuration;
using System.Web;

namespace CSASPNETResumeDownload
{
    public class DownloadHttpHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string filePath = ConfigurationManager.AppSettings["FilePath"];
            Downloader.DownloadFile(context, filePath);
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}
person Mohsen Afshin    schedule 15.04.2014

Если вы можете рассмотреть возможность использования веб-API ASP.NET, взгляните на мой пост Служба загрузки файлов ASP.NET Web API с поддержкой возобновления работы

Он предоставляет решение, использующее два разных подхода: класс FileStream и файлы с отображением памяти (это может дать некоторые преимущества в производительности).

person Piotr Walat    schedule 18.10.2012
comment
ссылка не работает и возвращает 502 - person PurTahan; 17.12.2020