0

I have a website which requires a web form with multiple uploads which get emailed to someone. To fix this we've created a temporary file-stream which basically copies the upload to the file-system and removes the file when disposed. The TemporaryFileStream inherits the Stream object to trick dotnet into thinking it actually is a stream, that way we can use var attachment = new Attachment(fileStream, file.FileName, file.ContentType); for the emails. The problem I'm having is that when the file is larger than 10 MB or if the total of the Files is larger than 10 MB I get a System.IO Error saying you cannot access a closed file. I don't understand why this only happens when the size is larger than 10 MB, it's not the upload because the HttpPostedFileBase gets send correctly, the error occurs when actually calling the `Read()' method on the temporary filestream.

This is how the temporary file stream is initiated:

    /// <summary>
    /// A temporary fileStream that is removed when disposed
    /// </summary>
    /// <param name="file">The HttpPosted file to write to the temp directory</param>
    public TemporaryFileStream(HttpPostedFileBase file)
    {
        file.InputStream.Flush();
        file.InputStream.Position = 0;

        _tempPath = Path.Combine(Path.GetTempPath(), String.Join(".", Path.GetRandomFileName(), file.FileName));
        _innerStream = File.Create(_tempPath);

        file.InputStream.CopyTo(_innerStream);
        _innerStream.Flush(true);
        _innerStream.Position = 0;        
        _innerStream.Unlock(0,_innerStream.Length);
    }

The http input stream is copied to the temp folder instantly and read later.

After sending the mail, the dispose() method is called by code not by using a using() wrapper.

    public new void Dispose()
    {
        if (!_baseDisposed)
        {
            _baseDisposed = true;
            Close();
            base.Dispose();
        }
        if (!_innerDisposed)
        {
            _innerDisposed = true;
            _innerStream.Flush();
            _innerStream.Close();
            _innerStream.Dispose();
        }
        if (File.Exists(_tempPath))
            File.Delete(_tempPath);
    }

This is the only place where Close() is called but it gets called after sending the mail.

I'm pretty sure it's not the httpRuntime.maxRequestLength setting because we've set that to: "512000".

I've even tried the following without success:

httpRuntime maxRequestLength="512000" executionTimeout="3600" 
 appRequestQueueLimit="100" requestLengthDiskThreshold="10024000" 
 enableKernelOutputCache="false" relaxedUrlToFileSystemMapping="true" />

Is there any reason why my filestream can be closed outside of my controll? Or could the async context of the SendMail(...).ConfigureAsync(true) have something to do with this?

With kind regards, Marvin Brouwer.

Marvin Brouwer
  • 306
  • 3
  • 13
  • You're not showing the code that calls `SendMail`? – Gary McGill Nov 09 '15 at 12:27
  • you should increase maxRequestLength http://stackoverflow.com/questions/288612/how-to-increase-the-max-upload-file-size-in-asp-net – Kishore Sahasranaman Nov 09 '15 at 12:29
  • 1
    When implementing Dispose(), [try to follow the pattern](http://stackoverflow.com/a/538238/98422). I'm not sure why you've got the `new` keyword there... – Gary McGill Nov 09 '15 at 12:32
  • I've updated my answer to clarify your questions. @GaryMcGill the `new` keyword is because the class inherit's the dotnet `Stream` class. – Marvin Brouwer Nov 09 '15 at 13:01
  • 1
    @MarvinBrouwer: then surely you want `override`, not `new`, since otherwise if any framework code calls `Dispose`, it'll call the base-class version, not your version? – Gary McGill Nov 09 '15 at 14:00
  • _"Is there any reason why my filestream can be closed outside of my control?"_ -- Any reason? Well, sure. There must be, at least if what you say is happening is, in fact, happening. There are all kinds of reasons actually, especially given the relative lack of detail in your question. Too many to list in a good answer. Please provide [a good, _minimal_, _complete_ code example](http://stackoverflow.com/help/mcve) that reliably reproduces the problem. – Peter Duniho Nov 10 '15 at 04:37
  • @GaryMcGill, `override` is not an option the `Close()` method is not virtual so it's not overridable. – Marvin Brouwer Nov 13 '15 at 09:40
  • @MarvinBrouwer: I was talking about the `Dispose()` method, which **is** virtual. – Gary McGill Nov 13 '15 at 10:35

1 Answers1

0

I've solved the problem. It turns out to be the combination of base.Dispose(); and
httpRuntime maxRequestLength="512000" executionTimeout="3600" appRequestQueueLimit="100" requestLengthDiskThreshold="10024000" enableKernelOutputCache="false" relaxedUrlToFileSystemMapping="true" /> Apparently base.Dispose(); tried to close a stream that wasn't open and the requestLengthDiskThreshold etc. closed the filestream while reading.

Marvin Brouwer
  • 306
  • 3
  • 13