WebResponse:
WebRequest request = HttpWebRequest.Create("http://example.com/file.txt");
using (WebResponse response = request.GetResponse())
{
using (StreamReader reader = new
StreamReader(response.GetResponseStream()))
{
}
}
( ) Seek, . - "" , . StreamReader.
FTP , FtpWebRequest HttpWebRequest. a WebResponse GetResponse.
FTP- REST (restart), - (.. ). HTTP, HttpWebRequest.AddRange, Range ( HTTP 1.1).
, HTTP:
public class RangedHttpWebStream : Stream
{
private Stream realStream;
private long startPosition;
private long? requestedLength;
private HttpWebRequest request;
public RangedHttpWebStream(HttpWebRequest request)
{
if (request == null)
throw new ArgumentNullException("request");
this.request = request;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return (realStream == null); }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush()
{
}
public override long Length
{
get { return requestedLength ?? -1; }
}
public override long Position
{
get { return startPosition; }
set { Seek(value, SeekOrigin.Begin); }
}
public override int Read(byte[] buffer, int offset, int count)
{
if (realStream == null)
{
UpdateRange();
WebResponse response = request.GetResponse();
realStream = response.GetResponseStream();
}
return realStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (realStream != null)
throw new InvalidOperationException("Seek cannot be performed " +
"once reading has started.");
switch (origin)
{
case SeekOrigin.Begin:
startPosition = offset;
break;
case SeekOrigin.Current:
startPosition += offset;
break;
default:
throw new NotSupportedException("Seek can only be performed " +
"from the beginning of the stream or current position.");
}
return startPosition;
}
public override void SetLength(long value)
{
if (value < 0)
throw new ArgumentOutOfRangeException("Parameter 'value' " +
"cannot be less than zero.");
if (value > Int32.MaxValue)
throw new ArgumentOutOfRangeException("Parameter 'value' " +
"cannot be greater than Int32.MaxValue.");
requestedLength = value;
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("The stream does not support writing.");
}
protected override void Dispose(bool disposing)
{
if ((disposing) && (realStream != null))
realStream.Dispose();
base.Dispose(disposing);
}
private void UpdateRange()
{
if (startPosition < 0)
throw new IOException("Attempted to seek before " +
"beginning of stream.");
if (startPosition > Int32.MaxValue)
throw new IOException("Attempted to seek past Int32.MaxValue. " +
"This is invalid for an HTTP stream.");
if (requestedLength != null)
{
long endPosition = startPosition + requestedLength.Value;
if (endPosition > Int32.MaxValue)
throw new IOException("Attempted to read past " +
"Int32.MaxValue. This is invalid for an HTTP stream.");
request.AddRange((int)startPosition, (int)endPosition);
}
else
{
request.AddRange((int)-startPosition);
}
}
}
- - , / .
HttpWebRequest, :
public static class HttpExtensions
{
public static Stream GetSmartStream(this HttpWebRequest request)
{
return new RangedHttpWebStream(request);
}
}
The testing program (actually tested) is as follows:
static void Main(string[] args)
{
var request = (HttpWebRequest)HttpWebRequest.Create(
"http://localhost/test.txt");
using (Stream stream = request.GetSmartStream())
{
stream.Seek(20, SeekOrigin.Begin);
stream.Seek(1, SeekOrigin.Current);
stream.SetLength(100);
using (StreamReader reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
Console.Write(content);
}
}
Console.ReadLine();
}
This is basically a copy-paste operation for FTP. The corresponding request property for modifying FtpWebRequest.ContentOffset . Unlike HTTP, you cannot set the final offset, so you have to change the property SetLengthto throw NotSupportedException.