2

In my Silverlight application I need to download large files. I am currently streaming this data from a byte array by calling an ASPX page on the same server hosting the Silverlight app. The ASPX Page_Load() method looks like this:

protected void Page_Load(object sender, EventArgs e)
{
  // we are sending binary data, not HTML/CSS, so clear the page headers
  Response.Clear();
  Response.ContentType = "Application/xod";

  string filePath = Request["file"];  // passed in from Silverlight app

  //  ...

  using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
  {
    // send data 30 KB at a time
    Byte[] t = new Byte[30 * 1024];

    int bytesRead = 0;
    bytesRead = fs.Read(t, 0, t.Length);
    Response.BufferOutput = false;

    int totalBytesSent = 0;

    Debug.WriteLine("Commence streaming...");
    while (bytesRead > 0)
    {
      // write bytes to the response stream
      Response.BinaryWrite(t);

      // write to output how many bytes have been sent
      totalBytesSent += bytesRead;
      Debug.WriteLine("Server sent total " + totalBytesSent + " bytes.");

      // read next bytes
      bytesRead = fs.Read(t, 0, t.Length);
    }
  }

  Debug.WriteLine("Done.");

  // ensure all bytes have been sent and stop execution
  Response.End();
}

From the Silverlight app, I just hand off the uri to object that reads in the byte array:

Uri uri = new Uri("https://localhost:44300/TestDir/StreamDoc.aspx?file=" + path);

My question is... How do I stop this stream if the user cancels out? As it is now, if the user selects another file to download, the new stream will start and the previous one will continue to stream until it has completed.

I can't find a way to abort the stream once its been started.

Any help is greatly appriciated.

-Scott

Scott
  • 874
  • 3
  • 12
  • 36
  • Aside from anything else, you're always *assuming* that the Read call fills the buffer, because you're then writing out *the whole buffer* rather than the bytes just read. – Jon Skeet Aug 27 '11 at 20:51
  • Not sure I follow, but the buffer will never have more than 30k in it, and yes, I then send the entire 30k (or less) back to the Response channel. – Scott Aug 27 '11 at 21:01
  • 3
    But it might have *less* than 30K of useful data in it. You should only be writing `bytesRead` bytes in each iteration, not the whole 30K. – Jon Skeet Aug 27 '11 at 21:03
  • Ahh, I see what you're saying now. Good eye - I'll work on fixing that. Actually, I'm a bit shocked that it's working at all since there could be garbage between the bytes read and the 30k limit on each iteration. I'm guessing I was "lucky" and had the full 30k read each time in my testing. – Scott Aug 27 '11 at 21:10
  • reading from local disk will usually fill the buffer. But your last iteration would almost always have been wrong - you've probably got a lot of files which have trailing garbage, as it were. In some file formats that doesn't matter of course. – Jon Skeet Aug 27 '11 at 21:15
  • What version of IIS are you targeting? If IIS7 is the app pool hosting the site running an Integrated pipeline? – AnthonyWJones Aug 27 '11 at 21:18
  • Currently I'm running this in IIS Express on my dev box. I am using IIS7 on the production server and I am using Integrated Pipleline in the hosting App Pool. – Scott Aug 27 '11 at 21:30

3 Answers3

0

If you are sure it's only going to be 30K of data, you might consider simplifying it with File.ReadAllBytes.

Peter Bromberg
  • 1,498
  • 8
  • 11
0

If you abort the request on the client, using HttpWebRequest.Abort (like in this answer) then a ThreadAbortException should get raised on the server in response to the end of the TCP connection, which will stop that thread writing out data.

Community
  • 1
  • 1
Douglas
  • 36,802
  • 9
  • 76
  • 89
0

I just hand off the uri to object that reads in the byte array

I'll assume for the moment you are simply using the WebClient. The WebClient has a CancelAsync method. The eventargs of OpenReadCompleted has a Cancelled property you can test.

When the client aborts a connection the server will not send any more bytes but server code will continue to run, its the internals of IIS which will simply discard the buffers it receives since it no longer has anywhere to send them.

On the server you can use the IsClientConnected property of the HttpResponse object to determine whether to abort your pump loop.

BTW, You really should consider moving this code to a .ashx, an .aspx carries a lot of baggage that you don't need.

AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306