Как программно записать начало и конец MVC FileResult для регистрации неполных загрузок? - PullRequest
3 голосов
/ 26 апреля 2010

Я изучал, как приложение ASP.NET MVC может регистрировать неудачные / неполные загрузки. У меня есть контроллер, обрабатывающий запросы файлов, который в настоящее время возвращает FileResult. Что мне нужно записать, это IP-адрес, имя файла и когда загрузка началась, затем те же данные и когда она была завершена. Я пытался перехватить начало и конец запроса с помощью HttpModule, а также IIS7 - не удалось отследить запрос, но не уверен, какой маршрут был бы лучшим, и у меня такое ощущение, что, возможно, я упускаю очевидный ответ на проблему.

Кто-нибудь имеет какие-либо предложения или знает какие-либо альтернативы, так как это похоже на то, что многие люди хотели бы узнать со своего веб-сервера?

Спасибо за вашу помощь

Ответы [ 3 ]

2 голосов
/ 26 апреля 2010

Можно попробовать написать пользовательский FilePathResult и переопределить WriteFileMethod :

public class CustomFileResult : FilePathResult
{
    public CustomFileResult(string fileName, string contentType)
        : base(fileName, contentType)
    { }

    protected override void WriteFile(HttpResponseBase response)
    {
        // TODO: Record file download start
        base.WriteFile(response);
        // TODO: Record file download end
    }
}

и в вашем контроллере:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return new CustomFileResult(@"d:\test.jpg", "image/jpg");
    }
}
1 голос
/ 06 мая 2010

ОК, поэтому, попробовав первый ответ, он не сработал как вызов base.WriteFile (response); работает асинхронно.

С тех пор я написал расширение класса FilePathResult, которое работает посредством потоковой передачи ответа. Я также добавил простую поддержку возобновления файла, используя инструкцию заголовка диапазона.

public class LoggedFileDownload : FilePathResult
    {
        private readonly IRepository repository;
        private readonly AssetDownload assetDownload;
        public LoggedFileDownload(string fileName, string contentType, string downloadName, IRepository repository, AssetDownload assetDownload) : base(fileName, contentType)
        {
            FileDownloadName = downloadName;
            this.repository = repository;
            this.assetDownload = assetDownload;
        }

        protected override void WriteFile(HttpResponseBase response)
        {
            long totalSent = 0;
            long bytesRead = 0;
            var fileInfo = new FileInfo(FileName);
            var readStream = fileInfo.OpenRead();
            var buffer = new Byte[4096];

            long responseLength = readStream.Length;
            var rangeHeader = HttpContext.Current.Request.Headers["Range"];

            if (!rangeHeader.IsNullOrEmpty())
            {

                string[] range = rangeHeader.Substring(rangeHeader.IndexOf("=") + 1).Split('-');

                long start = Convert.ToInt64(range[0]);
                long end = 0;

                if (range[1].Length > 0) end = int.Parse(range[1]);

                if (end < 1) end = fileInfo.Length; 

                if (start > 0)
                {
                    responseLength -= start;
                    readStream.Seek(start, 0);
                    totalSent += start;
                    var rangeStr = string.Format("bytes {0}-{1}/{2}", start, end, fileInfo.Length);
                    response.StatusCode = 206;
                    response.AddHeader("Content-Range",rangeStr);
                }
            }

            response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", FileDownloadName));
            response.AddHeader("Content-MD5", GetMD5Hash(fileInfo));
            response.AddHeader("Accept-Ranges", "bytes");
            response.AddHeader("Content-Length", (responseLength).ToString());
            response.AddHeader("Connection", "Keep-Alive");
            response.ContentType = FileTypeHelper.GetContentType(fileInfo.Name);
            response.ContentEncoding = Encoding.UTF8;

            response.Clear();

            while(response.IsClientConnected && (bytesRead = readStream.Read(buffer, 0, buffer.Length)) != 0 )
            {
                totalSent += bytesRead;
                response.BinaryWrite(buffer);
                response.Flush();
            }

            if (totalSent == fileInfo.Length)
            {
                // This means the file has completely downloaded so we update the DB with the completed field set to true
                assetDownload.Completed = true;
                repository.Save(assetDownload);
                repository.Flush();
            }

        }

        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();
        }


    }
0 голосов
/ 29 марта 2016

Это можно сделать более просто: используйте Response.TransmitFile () и (ВАЖНО!) Установите Response.BufferOutput = false. Значение BufferOutput по умолчанию равно true - это означает, что выходные данные буферизируются и будут отправлены после завершения обработки всей страницы.

Контроллер:

// record download start event here
...

// send a file to Response, not buffered.
// In my case the downloaded file has name "setup.exe"
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", @"filename=""setup.exe""");
Response.BufferOutput = false;
Response.TransmitFile(fileURL); 

// record download finish event here
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...