36

Im trying to prompt a downloadable text file (.txt), but I get this error:

Cannot access a closed Stream.

I have looked at simular questions in here: Cannot Access Closed Stream But it was not very useful.

Here is my code:

    private FileStreamResult Export()
    {
        string name = "filename.txt";

        MemoryStream stream = new MemoryStream();
        using (StreamWriter writer = new StreamWriter(stream))
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("A text...");
            writer.WriteLine(sb.ToString());
        }

        return File(stream, "text/plain", name);
    }

UPDATE (working copy):

This gives me an blank text file.

private FileResult Export()
{
    string name = "filename.txt";

    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);

    StringBuilder sb = new StringBuilder();
    sb.Append("A text...");
    writer.WriteLine(sb.ToString());

    writer.Flush();
    stream.Seek(0, SeekOrigin.Begin);

    return File(stream, "text/plain", name);
}
Community
  • 1
  • 1
Martin at Mennt
  • 5,677
  • 13
  • 61
  • 89

4 Answers4

59

That is correct, when you wrap a stream in another stream, calling .Close() or .Dispose() on any of them will dispose the whole stream. In this case, wrapping the MemoryStream in a StreamWriter means that when the using statement completes the StreamWriter and MemoryStream are both disposed.

My guess is since you are returning a FileStreamResult the encapsulating File will close the stream for you after the stream is no longer used. In this case, you do not want to use the using statement and will want to leave the stream open when returning it.

UPDATE

Since a stream is forward access you'll need to see the stream back to the beginning to allow the data to be read back out.

stream.Seek(0, SeekOrigin.Begin);
Joshua
  • 8,112
  • 3
  • 35
  • 40
  • Thanks, see my reply to #archil. – Martin at Mennt Aug 22 '11 at 20:04
  • 2
    Since it's a memory stream you may need to [Seek](http://msdn.microsoft.com/en-us/library/system.io.memorystream.seek.aspx) to the beginning of the stream. See my update. You're getting zero bytes because there is no data at the current position of the stream, at the end. :) – Joshua Aug 22 '11 at 20:50
13

Just remove that using statement - you are passing disposed object reference to File method and you that's the reason why you get exception. From MSDN,

The StreamWriter object calls Dispose on the provided Stream object when StreamWriter.Dispose is called.

I believe File will dispose stream by itself after usage (not verified by looking at source code).

UPDATE:

writer.Flush(); before return statement should help you

archil
  • 39,013
  • 7
  • 65
  • 82
  • Thank you both #archil and #Joshua. I tried to remove the using, but now I get a empty text file. What did I do wrong? I have updated my question with the new code. – Martin at Mennt Aug 22 '11 at 20:03
  • I wish for it to work, but the txt file is still at 0 byte. And I see that the stream have positive length, so it has to be something there. Strange.. – Martin at Mennt Aug 22 '11 at 20:13
5

You have to set the position of the memorystream to 0 before using it in your FileStreamResult, otherwise it will be read from current position (IE the end of the stream).

stream.Position = 0;
return File(stream, "text/plain", name);

Just had the same thing.

I know this thread is ancient, just hoping to aid others having the same issue.

Julian
  • 1,050
  • 1
  • 12
  • 23
  • It's always the little things. I was getting an empty file but it was the correct "lenght".. the position was the fix. seems to me but when creating a new FileStreamResult from a MemoryStream, it should know better. – da_jokker Oct 26 '18 at 14:32
  • I agree (somewhat) - Overloads for known stream-types (which support setting position) would be helpful for most cases, though it smells like a leaky abstraction to me. – Julian Oct 28 '18 at 09:52
  • 1
    I think a simple 'Input-Stream is read from it's current position and forwards'-note in the summary of it's ctor would go a long way... – Julian Oct 28 '18 at 09:56
1

Replace the FileStreamResult type on your action with FileResult.

mare
  • 13,033
  • 24
  • 102
  • 191