Hi,
Many thanks for this! I've adapted it slightly so that it works on a FileStream. You pass in the name of the file that you want to serve to the client and it establishes a stream to the file and serves it out. It woulnd't be very difficult to modify it slightly more so that it will support HTTP/1.1 chunked encoding.
It's very easy to modify the examples provided to serve a file rather than a string. You will need to ensure that you send the Content-Length header though, otherwise your browser will sit there forever waiting for more data to arrive - implementing chunked encoding would solve this.
Call the FileProducer (below) using
httpResponse.OnResponse(responseHeaders, new FileProducer(fileName));class FileProducer : IDataProducer
{
// Members
private string m_fileName;
private FileStream m_fileStream;
private IDataConsumer m_consumer;
// Constructor
public FileProducer(string fileName)
{
// The stream that we'll serve up
m_fileStream = null;
// Check that the file that we are going to serve actually exists
if (File.Exists(fileName) == false)
{
throw new Exception("File does not exist");
}
else
{
m_fileName = fileName;
}
}
// Returns either a continuation or NULL if no continuation
public IDisposable Connect(IDataConsumer channel)
{
// Store a handle to the data consumer
m_consumer = channel;
// Open the file that we want to send
m_fileStream = File.OpenRead(m_fileName);
// Send data
Send();
// Return a handle to a disconnector
return new FileDisconnector(m_fileStream);
}
private void Send()
{
// Buffer to hold bytes read from file
byte[] buffer = new byte[1024];
// If the file is closed
if (m_fileStream.CanRead == false)
{
return;
}
// Set up an async callback to write data to the consumer
m_fileStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(x =>
{
// Wait for a read on the file to complete
int bytesRead = m_fileStream.EndRead(x);
// Will the consumer invoke continuation?
bool waitOnClient = m_consumer.OnData(new ArraySegment<byte>(buffer, 0, bytesRead), Send);
// If we've hit the end of the stream
if (m_fileStream.Position >= m_fileStream.Length)
{
m_fileStream.Close();
m_consumer.OnEnd();
return;
}
// If we aren't waiting on the client to request more data
else if (waitOnClient == false)
{
Send();
}
}), null);
}
}
class FileDisconnector : IDisposable
{
// Members
private FileStream m_fileStream;
// Constructor
public FileDisconnector(FileStream fileStream)
{
m_fileStream = fileStream;
}
public void Dispose()
{
m_fileStream.Close();
}
}