The task at hand
At some point as a developer, you might need to combine multiple Sitecore Media Library items into a compressed ZIP and deliver it on the fly to the end-user. In my use case, I had a client that wanted to build out an asset library for the press to use when creating print and online news stories. They wanted to give their users a way to download all of the images for a given press release in one click. In this post, I’ll document how I approached that task using a custom route in .NET supported by an ActionResult in a Controller.
In my scenario, I had a Sitecore item that has a list of images attached to it. My goal was to take that list and create a zip file out of it. Even though I was using a list that allowed for easy updating in the UI, if we switch over to raw values we’ll see something similar to this:
Sitecore is combining the items in the list and concatenates them together with a pipe for separation between the GUIDs. Knowing this, we can target that field and do a split on the pipe. From there we can go on about the work of packing up the files. The first step for me was to create a new handler that would be able to take an ID of an item and then parse out that field so I knew which items to package up.
Creating a custom Route and ActionResult
Here’s an example of a custom route to a controller action:
routes.MapRoute("Download", "download /{ItemId}", new { controller = "Media", action = "Download" }, new string[] { @"MySite.Controllers" });
Then in my controller I can create an ActionResult to get the values we’re looking to work with:
public ActionResult Download(string itemId) { var parentItem = _scHelper.GetItem(itemId); var imageItems = parentItem.Fields[“YOUR_FIELD_GUID”].Value.Split('|'); return Content(“OK”); }
We’re only returning a string at this point, but we’ve got the code in place to know which items we’d like to work with. We can then do a foreach for the imageItems variable to retrieve the data we need from the MediaManager and add it to the zip file we’re creating. First, we’ll want to create a MemoryStream for us to work with, and then using that stream we can create a ZipArchive for our files.
Creating the Memory Stream and ZIP file
Here’s how we can create a MemoryStream, add a ZIP file within it, and return it as a file:
using (System.IO.MemoryStream zipStream = new System.IO.MemoryStream()) { using (System.IO.Compression.ZipArchive zip = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Create, true)) { // We can add our files here } // Create a byte array to convert our stream to Byte[] bytes = zipStream.ToArray(); // Deliver the array as a file return File(bytes, "application/zip", "YourCompressedFile.zip"); }
Including the files we want in our ZIP
At this point we know which files we want to include and we have a blank ZIP file. All that’s left is for us to do is to loop through the list of images, retrieve their data from Sitecore, and include them in the ZIP file that we’re building.
We can retrieve the data for the images with the MediaManager in Sitecore:
foreach (var image in imageItems) { var mediaItem = (MediaItem)_scHelper.GetItem(image); var media = MediaManager.GetMedia(mediaItem); var stream = media.GetStream().Stream; var extension = mediaItem.Extension; if (String.IsNullOrEmpty(extension)) continue; ZipArchiveEntry zipItem = zip.CreateEntry(mediaItem.Name + "." + extension); using (System.IO.Stream entryStream = zipItem.Open()) { stream.CopyTo(entryStream); } }
It’s that easy! The code should probably be updated to add a bit of error handling and you’ll probably want a better name than YourCompressedFile.zip as the name of your file. Hopefully this has given you an idea of how you can take advantage of raw values and the MediaManager in Sitecore to save your users a some time by being able to download a single file instead of multiple downloads.