проблема с возможностью возобновления загрузки файла -> когда размер файла превышает Int32.MaxValue (2 ГБ) (большие файлы)

#c# #asp.net #file #download #resume

#c# #asp.net #файл #Скачать #возобновить

Вопрос:

я нашел приведенный ниже код в stackoverflow для возобновляемой загрузки файлов :

 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;
using System.Net;

namespace WindowsServer.Classes
{
    public class DownloadFile
    {

        public static bool DownloadFileMethod_2(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" amp;amp;
                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 amp;amp; 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_2(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(" ", " "));
                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 amp;amp; 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;
        }

        static string GetMD5Hash_2(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i  )
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

    }
}
  

этот метод вернет false, когда размер файла больше, чем Int32.MaxValue!
как мы можем изменить этот метод для больших файлов, таких как 10 ГБ или более?

Комментарии:

1. похоже, что парень был уничтожен из-за этого вопроса… (бедняга) (так грустно за него!) / но, дорогие друзья, мы здесь для того, чтобы учиться, а не для того, чтобы голосовать против и банить вместе из-за одного вопроса!

Ответ №1:

Он возвращает false, потому что код содержит возвращаемое значение false, если размер файла больше Int32.MaxValue:

  var fileLength = myFile.Length;
        if (fileLength > Int32.MaxValue)
        {
           response.StatusCode = 413;
            return false;
        }
  

Это ответ на ваш вопрос, или вы хотите знать, почему в исходный код было добавлено это предложение? Если вы не понимаете почему, вы уверены, что хотите использовать этот случайный код, найденный в intertubez?

Комментарии:

1. простая отладка также выявила бы эту проблему.

2. привет, спасибо за ответ! я понял этот код и знаю, что это условие никогда не будет выполнено! но я хочу показать этот метод профессионалам, чтобы получить лучший метод для загрузки файлов размером более 2 ГБ! (имеется в виду редактирование и настройка)

Ответ №2:

  var fileLength = myFile.Length;
  

Жаль, что var это было использовано здесь. fileLength является long , и этого будет достаточно.

   if (fileLength > Int32.MaxValue)
  {
      response.StatusCode = 413;
      return false;
  }
  

Кажется ненужным.

Комментарии:

1. Какое отношение к этому имеет var аспект?

2. @gabe Не все знают Stream. Длина большая. Я бы никогда не использовал var здесь.

3. Я не знал, что это long , но если бы это было int , fileLength > Int32.MaxValue это никогда не было бы правдой.

4. спасибо, ребята, за помощь, ответы и комментарии… вы правы! кажется, это никогда не было бы правдой. но я просто хочу знать, почему это условие существует?

5. @MoonLight Это будет верно для файлов > 2 ГБ. Вам следует удалить его и протестировать этот метод снова.