-2

I have a MemoryStream within a using statement that I pass as a parameter to another method, however the 2nd time the method is called, the memory stream is closed. Below is the error and the code. Any idea what could cause this?

Cannot access a closed stream.

    public async Task UploadObject(IFormFile file, string prefix)
    {
        using (var memoryStream = new MemoryStream())
        {
            file.CopyTo(memoryStream);
            await UploadImageAsync(memoryStream, false));
            await UploadImageAsync(memoryStream, true));
        }
    }

    public async Task UploadImageAsync(MemoryStream stream, bool process)
    {
        if (process)
        {
            using (Image orginalImage = Image.FromStream(stream)) **<<<< ERROR HERE**
            {
                using (Bitmap bitmapResized = new Bitmap(orginalImage, newWidth, newHeight))
                {
                    using (MemoryStream streamResized = new MemoryStream())
                    {
                        bitmapResized.Save(streamResized, orginalImage.RawFormat);

                        await UploadAsync(streamResized);
                    }
                }
            }
        }
        else
        {
            await UploadAsync(stream);
        }

        async Task UploadAsync(MemoryStream stream)
        {
            // stream used here to create uploadrequest
            await fileTransferUtility.UploadAsync(uploadRequest);
        }
    }
Primico
  • 2,143
  • 3
  • 24
  • 36

1 Answers1

0

There's a couple of things:

  1. stream.CopyTo(otherStream) does nor reset the position of the target stream, Image.FromStream(otherStream) may only be looking at the stream after the current position, which is to say an empty stream. As a best practice when working with streams, use stream.Seek(0, SeekOrigin.Begin) to set the position to the beginning before passing the stream to another function.

  2. Your file transfer util is prematurely closing the underlying stream. figure out why that's happening, then you should be able to resolve the issue.

EDIT: Given that you're stream is closed by 3rd party code, you can't just fix the util itself. If you control the code in question, it's better to add a keepOpen param to the method to let you opt in to not disposing the stream.

Lacking that control, you can instead make a wrapper for the stream that should be left open that implements Stream and forwards all of the stream methods to the underling stream, except dispose which it will suppress.

  1. make a wrapper to keep an underling stream open by implementing stream and forwarding method implementations to the underlying object
public class KeepAliveStreamWrapper : Stream, IDisposable
{
    public KeepAliveStreamWrapper (Stream baseStream) {
        this.BaseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
    }
    
    public bool KeepAlive { get; set; } = true;
    
    public Stream BaseStream { get; }
    
    public override bool CanRead => this.BaseStream.CanRead;

    // other props here

    public override long Position { get => this.BaseStream.Position; set => this.BaseStream.Position = value; }

    public override void Flush() => this.BaseStream.Flush();

    // other functions here
    
    protected override void Dispose(bool disposing) 
    {
        if (KeepAlive) {
            // Keeping it alive, so suppress the dispose call
            return;
        }
        
        base.Dispose(disposing);
    }
}
  1. when passing the base stream to a function that would close it, use the wrapper
async Task UploadAsync(MemoryStream stream)
{
    using (var wrappedStream = new KeepAliveStreamWrapper (stream))
    {
        // stream used here to create uploadrequest
        await fileTransferUtility.UploadAsync(uploadRequest);
    }
}

When the file transfer utility tries to close the stream, the wrapper will not dispose the underlying memory stream.

Be careful that you still dispose the memory stream, you don't want to get into the habit of leaving streams open or you're going to get yourself a nasty bug. This just gives you more control of when you dispose it

J Scott
  • 781
  • 9
  • 12
  • The filetransferutility is an Amazon S3 library so if that utility is closing the stream, would my only solution be to clone the stream (if that is possible) each time I want to call it? – Primico Sep 15 '21 at 13:24
  • @Primico, you could make a new memory stream and populate with copy to, though I wouldn't recommend that for anything but the smallest files. Since you are working with images, you could also make a keep alive wrapper for a memory stream that implements stream by forwarding method calls to the underling memory stream. Disposing the wrapper would not close the underling stream, but disposing the memory stream directory would. – J Scott Sep 15 '21 at 16:17