This stream does not support search operations. HttpWebResponse

I am making a program that uploads files via http.

I have a download, however I want to be able to pause the download, close the program and resume it later.

I know where I download them, supports this.

I upload the file via HttpWebResponse and read the response in Stream using GetResponseStream.

When I close the application and restart it, I focus on how to resume downloading. I tried to do a search on a stream, but it claims that it is not supported.

What would be the best way to do this?

+6
c # stream download
source share
3 answers

If the server supports this, you should send the Range Http header with your request using the AddRange method:

request.AddRange(1024); 

This will instruct the server to start sending the file after the 1st kilobyte. Then just read the response stream as usual.

To check if the server supports resumption, you can send a HEAD request and check if it sends an Accept-Ranges: bytes header.

+9
source

What about the HTTPRangeStream class?

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

Your solution is fine, but it will only work if the server sends a Content-Length header. This header will not be present in dynamically generated content.

In addition, this solution sends a request for each Read. If the content changes on the server between requests, you will get conflicting results.

I would improve this by storing the data locally - either on disk or in memory. Then you can search in it whatever you want. There will be some kind of inconsistency problem, and you only need one HttpWebRequest to download it.

0
source

All Articles