简体   繁体   English

此流不支持搜索操作。 HttpWebResponse

[英]This stream does not support seek operations. HttpWebResponse

I'm making a program which downloads files over http. 我正在制作一个通过http下载文件的程序。

I've got it downloading, however I want to be able to pause the downloads, close the program and resume them again at a later date. 我已经下载了,但是我希望能够暂停下载,关闭程序并在以后再次恢复它们。

I know the location i'm downloading them from supports this. 我知道我从下载它们的位置支持这个。

I'm downloading the file through HttpWebResponse and reading the response into a Stream using GetResponseStream. 我正在通过HttpWebResponse下载文件,并使用GetResponseStream将响应读入流中。

When i close the app and restart it, I'm stuck as to how resume the download. 当我关闭应用程序并重新启动它时,我不知道如何恢复下载。 I've tried doing a seek on the stream but it states its not supported. 我已尝试在流上进行搜索,但它声明它不受支持。

What would be the best way to do this? 最好的方法是什么?

If the server supports this you have to send the Range Http header with your request using the AddRange method: 如果服务器支持此功能,则必须使用AddRange方法向您的请求发送Range Http标头:

request.AddRange(1024);

This will instruct the server to start sending the file after the 1st kilobyte. 这将指示服务器在第1千字节后开始发送文件。 Then just read the response stream as normal. 然后正常读取响应流。

To test if a server supports resuming you can send a HEAD request and test if it sends the Accept-Ranges: bytes header. 要测试服务器是否支持恢复,您可以发送HEAD请求并测试它是否发送Accept-Ranges: bytes标头。

How about an HTTPRangeStream class? 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();
        }

    }
}

Your solution is fine, but it will only work for the cases where the server sends a Content-Length header. 您的解决方案很好,但它只适用于服务器发送Content-Length标头的情况。 This header will not be present in dynamically generated content. 此标头不会出现在动态生成的内容中。

Also, this solution is send a request for each Read. 此外,此解决方案是发送每个Read的请求。 If the content changes on the server between the requests, then you will get inconsistent results. 如果内容在请求之间在服务器上发生更改,那么您将得到不一致的结果。

I would improve upon this, by storing the data locally - either on disk or in memory. 我会通过在本地存储数据来改进这一点 - 无论是在磁盘上还是在内存中。 Then, you can seek into it all you want. 然后,你可以随心所欲地寻找它。 There wont be any problem of inconsistency, and you need only one HttpWebRequest to download it. 不存在任何不一致的问题,您只需要一个HttpWebRequest来下载它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM