这个流不支持寻址操作。HttpWebResponse

6
我正在制作一个通过http下载文件的程序。
我已经实现了下载功能,但是我希望能够暂停下载、关闭程序并在以后的某个时间恢复下载。
我知道我正在下载的位置支持这种操作。
我使用HttpWebResponse下载文件,并使用GetResponseStream将响应读入流中。
当我关闭应用程序并重新启动时,我不知道如何继续下载。我尝试在流上进行查找,但是它提示不支持此操作。
有什么最好的方法来实现这一点?

记住,你必须告诉服务器重新启动 - 你不能自己做。这就是下面AddRange答案的原因。 - John Saunders
3个回答

9
如果服务器支持,您需要使用AddRange方法在请求中发送Range Http标头:

request.AddRange(1024);

这将指示服务器在第一个千字节后开始发送文件。然后像平常一样读取响应流。
为了测试服务器是否支持恢复,您可以发送一个HEAD请求并测试它是否发送“Accept-Ranges:bytes”头。

5
你希望在 MSDN 文档中添加链接吗?http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.addrange.aspx - Jon Skeet

3
HTTPRangeStream类怎么样?
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace Ionic.Kewl
{
    public class HTTPRangeStream : Stream
    {
        private string url;
        private long length;
        private long position;
        private long totalBytesRead;
        private int totalReads;

        public HTTPRangeStream(string URL)
        {
            url = URL;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            HttpWebResponse result = (HttpWebResponse)request.GetResponse();
            length = result.ContentLength;
        }

        public long TotalBytesRead    { get { return totalBytesRead; } }
        public long TotalReads        { get { return totalReads; } }
        public override bool CanRead  { get { return true; } }
        public override bool CanSeek  { get { return true; } }
        public override bool CanWrite { get { return false; } }
        public override long Length   { get { return length; } }

        public override bool CanTimeout
        {
            get
            {
                return base.CanTimeout;
            }
        }


        public override long Position
        {
            get
            {
                return position;
            }
            set
            {
                if (value < 0) throw new ArgumentException();
                position = value;
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            switch (origin)
            {
                case SeekOrigin.Begin:
                    position = offset;
                    break;
                case SeekOrigin.Current:
                    position += offset;
                    break;
                case SeekOrigin.End:
                    position = Length + offset;
                    break;
                default:
                    break;
            }
            return Position;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.AddRange(Convert.ToInt32(position), Convert.ToInt32(position) + count);
            HttpWebResponse result = (HttpWebResponse)request.GetResponse();
            using (Stream stream = result.GetResponseStream())
            {
                stream.Read(buffer, offset, count);
                stream.Close();
            }
            totalBytesRead += count;
            totalReads++;
            Position += count;
            return count;
        }


        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
        public override void Flush()
        {
            throw new NotSupportedException();
        }

    }
}

即使服务器不接受恢复,这个类还能工作吗? - Smith
我不知道什么是“恢复”。HTTP 1.1定义了一个范围头,这个类依赖于它。如果服务器支持HTTP 1.1,则支持范围,并且可以被此类联系。 - Cheeso
我所说的“resuming”指的是“range header”,感谢澄清。 - Smith

0

你的解决方案很好,但它只适用于服务器发送Content-Length头的情况。这个头在动态生成的内容中不会出现。

此外,这个解决方案为每个读取操作发送一个请求。如果内容在请求之间在服务器上发生更改,则会得到不一致的结果。

我会对此进行改进,通过将数据存储在本地 - 在磁盘或内存中。然后,您可以随意查找它。不会有任何不一致的问题,并且您只需要一个HttpWebRequest来下载它。


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