0

My MVC/webApi calculates a report to be returned in a file as a stream. The calculation takes quite some time so in order to speed up the user experience it's cached. When it's returned the stream is automatically disposed. So in order to get the caching working it has to be copied over to a new Stream object every time.

Since I want to cache the outcome I don't want it to be disposed at all.

I don't know how the file could be cached through configuration because it's not a file on disk, it's calculated on demand based on input.

So how can MVC/WebApi be configured to not automatically dispose this response? It might be with the OutgoingResponseContext, but can't find anything in there that might do that.

Edit, added code so far: Code for returning a stream, deep copying it every time it would otherwise be passed as reference and returning it as a file.

public stream ConfigureResponseHeadersForFileStream(string input)
{
    // Calculate file
    var result = new CachedFile(input, CalculateFileContent());
    WebOperationContext.Current.OutgoingResponse.Headers.Clear();
    WebOperationContext.Current.OutgoingResponse.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    WebOperationContext.Current.OutgoingResponse.ContentLength = length;
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Content-Disposition", "attachment; filename=" + "input");
    _cacher.Add(result);
    return result.Content;
}

public class CachedFile
{
    private Stream _content;
    public string Input;

    public long Length
    {
        get
        {
            return _content.Length;
        }
    }

    /// <summary>
    /// Will be copied into a new caching object.
    /// Because the web environment will dispose the stream that's returned to the user.
    /// </summary>
    public Stream Content
    {
        get
        {
            var stream = new MemoryStream();
            _content.CopyTo(stream);
            return stream;
        }

        set
        {
            _content = new MemoryStream();
            value.CopyTo(_content);
        }
    }

    public CachedFile(string input, Stream content)
    {
        Input = input;
        Content = content;
    }
}
MrFox
  • 4,852
  • 7
  • 45
  • 81
  • Could you please provide some code? It would be easier for us to understand what you have accomplished and what to choose to solve your problem. – eocron Feb 02 '17 at 19:58
  • It doesn't make much sense to cache a stream, given the fact that a stream only holds a small buffer of data at a time. That said, your question is unclear. What are you trying to do? Please provide a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) and it will be much more likely someone can answer. – NightOwl888 Feb 02 '17 at 20:06
  • @eocron I haven't accomplished anything on solving this specific problem. Thought my theoretical explanation was pretty clear. – MrFox Feb 02 '17 at 22:12
  • @NightOwl888 The reason for caching is that it takes a long time to - calculate - the report. Several minutes. – MrFox Feb 02 '17 at 22:14
  • For this task you need to make wrapper around Stream methods which in fact will be writing in double streams - one into web, other into file. Then on same request you just read file stream. – eocron Feb 02 '17 at 22:17
  • You could probably [convert steam to byte array](http://stackoverflow.com/questions/221925/creating-a-byte-array-from-a-stream) and cache it, and [convert back to stream](http://stackoverflow.com/questions/9918692/how-to-convert-a-byte-array-to-stream#9918707). – praveen Feb 02 '17 at 22:23
  • Ok, you didn't mention before that it is a `MemoryStream`, which is basically a byte array that inherits `Stream`. But, since it is all in memory, it is not technically streaming anything. If you want your report to perform better, it is best if you do something along the lines of what @eocron suggested - stream the report into a file, close it, and then reopen it within the same request and stream it to the client. You then only need to cache the file's name (perhaps you can just load it into an invisible field on a view) instead of all of the report data. – NightOwl888 Feb 04 '17 at 09:04

0 Answers0