This blog post is long overdue. The code was written some time last year when I started my still on-going fight with Google Page Speed.
When Sitecore deliver svg files from the media library there is no out-of-the-box approach for gzipping the media content and setting the Content-encoding response header to gzip.
Gzipping the svg media content
First we need to gzip the media content.
To do this we insert the following processor into the end of the getMediaStream pipeline.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public class CompressStream { public void Process(GetMediaStreamPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); if (args.Options.Thumbnail) return; if (!IsCompressableService.IsCompressable(args.MediaData.Extension)) return; var outputStream = args.OutputStream; var compressed = Compress(WriteBytesToStream(outputStream.Stream)); var memoryStream = new MemoryStream(compressed); args.OutputStream = new MediaStream(memoryStream, args.MediaData.Extension, outputStream.MediaItem); } private static byte[] Compress(byte[] raw) { using (var memory = new MemoryStream()) { using (var gzip = new GZipStream(memory, CompressionMode.Compress, true)) { gzip.Write(raw, 0, raw.Length); } return memory.ToArray(); } } private static byte[] WriteBytesToStream(Stream sourceStream) { using (var memoryStream = new MemoryStream()) { sourceStream.CopyTo(memoryStream); return memoryStream.ToArray(); } } } |
1 2 3 4 5 6 7 |
<sitecore> <pipelines> <getMediaStream> <processor type="[NAMESPACE].CompressStream, [ASSEMBLY].MediaCompress" /> </getMediaStream> </pipelines> </sitecore> |
You will notice that we call a service class IsCompressableService which checks if the media stream should be compressed.
1 2 3 4 5 6 7 8 9 |
public class IsCompressableService { public static bool IsCompressable(string extension) { var settings = Settings.GetSetting("MediaCompress.CompressFileExtensions", "svg"); var compressFileExtensions = settings.Split(new[] {",", "|", ";"}, StringSplitOptions.RemoveEmptyEntries); return compressFileExtensions.Any(extension.Contains); } } |
This service reads a comma separated line from config:
1 2 3 4 5 |
<sitecore> <settings> <setting name="MediaCompress.CompressFileExtensions" value="svg" /> </settings> </sitecore> |
Now the media stream is gzipped, so we need to tell the browsers that the content is gzip encoded so they can understand what it is we are sending them.
Setting the Content-encoding response header
To do this we need to hook into the otherwise hidden event called media:request as I’ve also shown in this post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class MediaRequestEventHandler { public void OnMediaRequest(object sender, EventArgs args) { var sitecoreEventArgs = (SitecoreEventArgs) args; if (sitecoreEventArgs == null || !sitecoreEventArgs.Parameters.Any()) return; var request = (MediaRequest) sitecoreEventArgs.Parameters[0]; var media = MediaManager.GetMedia(request.MediaUri); if (!IsCompressableService.IsCompressable(media.Extension)) return; SetContentEncoding(request.InnerRequest.RequestContext.HttpContext.Response); } private static void SetContentEncoding(HttpResponseBase response) { if (HasGzipContentEncoding(response.Headers)) return; response.AddHeader("Content-encoding", "gzip"); } private static bool HasGzipContentEncoding(NameValueCollection headers) { return headers.AllKeys.Any(k => k.Equals("content-encoding", StringComparison.InvariantCultureIgnoreCase)) && headers["content-encoding"].Equals("gzip", StringComparison.InvariantCultureIgnoreCase); } } |
We first get the media item which is being requested and then we check using the file extension if the content should be compressed. If so we add Content-encoding: gzip as a response header.
The configuration for hooking into the event looks like this.
1 2 3 4 5 6 7 |
<sitecore> <events> <event name="media:request"> <handler type="[NAMESPACE].MediaRequestEventHandler, [ASSEMBLY].MediaCompress" method="OnMediaRequest" /> </event> </events> </sitecore> |
That was it. The same code can gzip any other text based content such as js, css, xml etc. but it is better to use IIS dynamic compression for this. If anyone have a tip on how to configure IIS 7 / 7.5 to dynamic compress svg files please share as long as it doesn’t imply changing the mime type to text/xml which is a terrible hack. It is possible to edit the applicationhost.config and set the mime-type here but this require you to have permissions (and time) to do this on all servers in the environment.
Please drop me a mail or a comment if you would like me to share the code as a project or a MarketPlace module.
Anders Laub Christoffersen
Anders has been working with Sitecore for over a decade and has in this time been the lead developer and architect on several large scale enterprise solutions all around the world. Anders was appointed the title of Sitecore Technical MVP in 2014 and has been re-appointed the title every year since then.
- Web |
- More Posts