42

how to compress the output send by an asp.net mvc application??

Praveen Prasad
  • 31,561
  • 18
  • 73
  • 106
  • Why not just set `` or even `` in `web.comfig` http://www.iis.net/configreference/system.webserver/httpcompression http://stackoverflow.com/questions/9235337/gzipping-content-files-in-asp-net-mvc-3? – angularrocks.com Dec 30 '13 at 00:56
  • You can also increase the performance by using compression and caching for the response data. Have a look at the following link :- http://weblogs.asp.net/rashid/asp-net-mvc-action-filter-caching-and-compression – Aishwarya Nagar Feb 09 '16 at 07:35

3 Answers3

94

Here's what i use (as of this monent in time):

using  System.IO.Compression;

public class CompressAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

        var encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
        if (string.IsNullOrEmpty(encodingsAccepted)) return;

        encodingsAccepted = encodingsAccepted.ToLowerInvariant();
        var response = filterContext.HttpContext.Response;

        if (encodingsAccepted.Contains("deflate"))
        {
            response.AppendHeader("Content-encoding", "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
        else if (encodingsAccepted.Contains("gzip"))
        {
            response.AppendHeader("Content-encoding", "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }
    }
}

usage in controller:

[Compress]
public class BookingController : BaseController
{...}

there are other varients, but this works quite well. (btw, i use the [Compress] attribute on my BaseController to save duplication across the project, whereas the above is doing it on a controller by controller basis.

[Edit] as mentioned in the para above. to simplify usage, you can also include [Compress] oneshot in the BaseController itself, thereby, every inherited child controller accesses the functionality by default:

[Compress]
public class BaseController : Controller
{...}
Juan Carlos Oropeza
  • 47,252
  • 12
  • 78
  • 118
jim tollan
  • 22,305
  • 4
  • 49
  • 63
  • actually, looked at your example - very similar indeed - spooky :). i've been using this code for over a year, so can verify that it works very well ... – jim tollan Sep 27 '10 at 09:31
  • is is possible i can do some settings in web.config to do the compression. one more thing i want to know, how to check howmuch overhead is added to server by compression code we are running here. – Praveen Prasad Sep 27 '10 at 11:51
  • praveen- unfortunately, a web.config solution isn't one that i'm aware of (i also think it would be too course grained to be flexible). as for memory usage, i think you may have to use some tool outside of IIS to measure that. – jim tollan Sep 27 '10 at 14:35
  • Could you include how you add it to your Base Controller? – Mark May 17 '13 at 16:01
  • Hi mark - it's actually as simple as just moving the `[Compress]` attribute down from the child controller to decorate the `BaseController` instead. I've added an amendment to show this – jim tollan May 17 '13 at 16:27
  • @jimtollan And then have every controller derive from your BaseController? Does this work for CSS and Javascript files? or just dynamic content? – Mark May 17 '13 at 16:36
  • Hi mark, yes, every controller that derives from BaseController will inherit this logic. this works for any content that can be gzipped, tho of course, some css and images etc may by default be pulled from local cache anyway. – jim tollan May 18 '13 at 12:42
  • Why not just use IIS `urlCompression` ? http://stackoverflow.com/questions/9235337/gzipping-content-files-in-asp-net-mvc-3?lq=1 – angularrocks.com Dec 30 '13 at 00:55
  • hey there, good point, tho the above, as mentioned in my answer (and actually also mentioned in the comments above re *web.config being too course grained!!*), gives an alternative for those who prefer to be able to roll in compression at a fine grained, individual controller level on a needs must basis (rather than across the board, which may not be desirable for the use case). :). happy new year when the hour approaches, wherever you are. hoots mon the noo... – jim tollan Dec 30 '13 at 11:44
  • 9
    I added it as a global filter, in my startup class I added `GlobalFilter.Filters.Add(new CompressionAttribute());` and it works #1! Also inverted the `if` clause to make sure `gzip` is used instead of `deflate` when both encodings are supported. – Charles Ouellet Jan 14 '15 at 18:52
  • 2
    Just wanted to add, that in case you didn't start by deriving from a base controller, don't be scared. You can always use an IoC container that will handle that. For example, in Ninject (MVC 5) you can use: "kernel.BindFilter(FilterScope.Controller, 0);" under the "RegisterServices(IKernel kernel)" method! – Jose A Oct 03 '15 at 17:46
  • Jose, so glad that this little Attribute approach is still working, 5 years later and 4 versions of MVC... I just love this tech for that reason, build and consolidate!! – jim tollan Oct 03 '15 at 20:05
  • 1
    Great answer. But why you put `deflate` option first, I change order so `gzip` is first and now give around 10% more compresion – Juan Carlos Oropeza Sep 09 '16 at 13:49
  • The one issue IU had with this solution is that it doesn't honour the order of the accept-encoding header. If you receive gzip, deflate the priority is GZIP? – Ed Broome Nov 14 '17 at 10:48
  • Thanks for a nice solution! I just ran into the problem that response.Filter is null - see this question for the solution to that: https://stackoverflow.com/questions/15067049/asp-net-mvc-response-filter-is-null-when-using-actionfilterattribute-in-regist – ylva May 16 '18 at 08:54
  • 1
    Important! You should check response.Filter for null before putting it in stream! I encountered issue, when locally everything is OK, but when deployed to Azure service it fails. – Yehor Androsov Jan 11 '19 at 17:42
4

Have a look at this article which outlines a nifty method utilizing Action Filters.

For example:

[CompressFilter]
public void Category(string name, int? page)

And as an added bonus, it also includes a CacheFilter.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
veggerby
  • 8,940
  • 2
  • 34
  • 43
3

For .NET Core 2.1 there is a new package that can be used ( Microsoft.AspNetCore.ResponseCompression )

Simple sample to get going, after installing the package:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCompression();

        services.AddResponseCompression(options =>
        {
            options.Providers.Add<GzipCompressionProvider>();
            options.EnableForHttps = true;
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseResponseCompression();
    }
}

You can read more about it here: https://learn.microsoft.com/en-us/aspnet/core/performance/response-compression?view=aspnetcore-2.1&tabs=aspnetcore2x

EKS
  • 5,543
  • 6
  • 44
  • 60