如何在下载文件时限制带宽并允许多个下载?

13

我有这段代码,当同时只有1个下载在运行时它是有效的

using (System.IO.FileStream fs = System.IO.File.OpenRead(@"C:\HugeFile.GBD"))
{
    using (System.IO.BinaryReader br = new System.IO.BinaryReader(fs))
    {
        Response.AddHeader("Cache-control", "private");
        Response.AddHeader("Content-Type", "application/octet-stream");
        Response.AddHeader("Content-Length", fs.Length.ToString());
        Response.AddHeader("Content-Disposition", "filename=HugeFile.GBD");
        Response.Flush();
        float kbs = 20f;

        while (fs.Position < fs.Length)
        {
            if (!Response.IsClientConnected)
                break;
            byte[] bytes = br.ReadBytes((int)Math.Truncate(1024 * kbs));
            char[] c = UTF8Encoding.Default.GetChars(bytes);

            Response.Write(c, 0, c.Length);


            Response.Flush();
            System.Threading.Thread.Sleep(1000);
        }
        Response.Flush();
    }
}

但是,如果我同时进行两个连接(在同一个浏览器上开始第二个下载),第二个连接直到第一个完成后才会执行。

使用线程或任务时,在向响应添加标头时会出现错误...

我该如何使得我能够在同一浏览器上同时执行2个或更多的下载?


也许可以考虑将其制作为 Task,并启动所需的任务。此外,您还可以查看此网站获取更多示例。 - Jon Peterson
我可能错了,但我认为您不必使用多线程。即使是大文件,服务器也应该自动处理。你如何测试它?如果他们从两台不同的机器上进行测试,但仍然存在相同的问题呢? - urlreader
1
+1,File.OpenRead 应该使用 FileAccess.Read,这将允许另一个进程或应用程序在此操作挂起时访问该文件。这可能不是我最初想到的文件访问限制。 - Brad Christie
2
你是说在同一时间同一个浏览器吗?比如在两个不同的标签页中? - Mike Perrenoud
1
我已经测试了Michael所说的内容,这也是我所经历的行为。无论你在方法中做什么,我都可以使用简单的重现方式:public ActionResult GetFile2() { Thread.Sleep(5000); return new ContentResult(); } 也就是说,在同一浏览器中连续调用两次,这些调用是顺序执行的。从IE和Chrome中调用,则这些调用是并行执行的。 - wal
显示剩余4条评论
1个回答

2

看起来 Google Chrome 使用唯一的 URL 处理下载,因此当我尝试访问相同的 URL 时,它甚至不会运行该操作。

通过在 URL 中添加任何内容,例如:

home/download?x=1
home/download?x=2
home/download?x=3

即使每次生成的文件不同,Chrome也会认为它是一个不同的下载并将其获取。因此,要下载文件,我们需要更改URL。

至于我创建的限速器,我创建了一个FileThrottleResult,它继承自FilePathResult

using System;
using System.IO;
using System.Threading;
using System.Web.Mvc;

namespace Mvc3Test
{
    public class FileThrottleResult : FilePathResult
    {
        float rate = 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="contentType"></param>
        /// <param name="rate">Kbps</param>
        public FileThrottleResult(string fileName, string contentType, float rate)
            : base(fileName, contentType)
        {
            if (!File.Exists(fileName))
            {
                throw new ArgumentNullException("fileName");
            }
            this.rate = rate;
        }

        protected override void WriteFile(System.Web.HttpResponseBase response)
        {
            int _bufferSize = (int)Math.Round(1024 * this.rate);
            byte[] buffer = new byte[_bufferSize];

            Stream outputStream = response.OutputStream;

            using (var stream = File.OpenRead(FileName))
            {
                response.AddHeader("Cache-control", "private");
                response.AddHeader("Content-Type", "application/octet-stream");
                response.AddHeader("Content-Length", stream.Length.ToString());
                response.AddHeader("Content-Disposition", String.Format("filename={0}", new FileInfo(FileName).Name));
                response.Flush();

                while (true)
                {
                    if (!response.IsClientConnected)
                        break;

                    int bytesRead = stream.Read(buffer, 0, _bufferSize);
                    if (bytesRead == 0)
                        break;

                    outputStream.Write(buffer, 0, bytesRead);
                    response.Flush();
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

使用方法:

public FileThrottleResult Download()
{
    string testFile = @"C:\hugefile.zip";
    return new FileThrottleResult(testFile, "application/octet-stream", 200);
}

只要你通过“不同”的URL欺骗浏览器,就可以成功限制带宽,而且不会出现同时下载同一文件的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接