1

In an ASP.Net MVC application of mine, I have a backend generating reports. Once the reports is generated, the user can download it with a link calling an action to serve it. However, I feel like the action to serve files is kinda slow (4 to 8 seconds), where the other action are instantaneous in comparaison.

Here is the code that does the serving

// GET: /WS/Report/GetReport
[HttpGet]
public ActionResult GetReport(string fileName)
{
    string path = Path.Combine(_reportGeneratedPath, fileName);
    if (!System.IO.File.Exists(path))
        return XmlMessage.Error("Report does not exist");
     Response.ContentType = MimeMapping.GetMimeMapping(fileName);
     Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}", fileName));
     Response.AddHeader("Content-Length", new FileInfo(path).Length.ToString());

    Response.WriteFile(path);
    Response.End();
    return null;
}

PS : I can't switch to a direct file access for security reasons.

Vinod
  • 1,882
  • 2
  • 17
  • 27
Remy Grandin
  • 1,638
  • 1
  • 14
  • 34

2 Answers2

0

System.IO.File.Exists will be slow because it is an IO operation. In order to speed your action, you can implement caching mechanism using MemoryCache which is part of System.Runtime.Caching. In addition, Response.WriteFile is a synchronous operation, If you switch to Response.TransmitFile which is asynchronous, it might improve your response time. This approach is a small incremental improvement for what you have, you can also go with streaming approach which will involve more changes to what you have.

Once your report generation is complete, you add the report full path to cache and then you can modify your Action method to something like (you can always improve your caching mechanism, what I have here is simple implementation, for more details please visit https://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx)

// GET: /WS/Report/GetReport
[HttpGet]
public ActionResult GetReport(string fileName)
{

    string path = Path.Combine(_reportGeneratedPath, fileName);
    var reportPathCache = MemoryCache.Default;
    if(!reportPathCache.Contains(path))
    {
      return XmlMessage.Error("Report does not exist");
    }
     Response.ContentType = MimeMapping.GetMimeMapping(fileName);
     Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}", fileName));
     Response.AddHeader("Content-Length", new FileInfo(path).Length.ToString());

    Response.Buffer = false;
    Response.TransmitFile(path);
    Response.End();
    return null;
}
Vinod
  • 1,882
  • 2
  • 17
  • 27
0

I've stripped it to "bare-bones"... set a single file into memory and downloading it from the action compared to downloading it from IHttpModule. IHttpModule is much faster (for small files, e.g. product list images) for some reason (probably MVC pipeline load). I don't have regex used in routing (that slows it even more). In IHttpModule I am reaching the same speeds as if URL is pointing to file on a drive (of course that is if the file is on the drive but not on the drive location that URL points to).

Hrvoje Batrnek
  • 535
  • 1
  • 5
  • 15