In my previous to last post, I discussed the Sitecore UploadWatcher — a Sitecore.IO.FileWatcher which uploads files to the Media Library when files are dropped into the /upload directory of your Sitecore website root.
Unfortunately, in the “out of the box” solution, files are uploaded directly under the Media Library root (/sitecore/media library). Imagine having to sift through all kinds of Media Library Items and folders just to find the image that you are looking for. Such would be an arduous task at best.
I decided to dig through Sitecore.Kernel.dll to see why this is the case, and discovered why: the FileCreated() method on the Sitecore.Resources.Media.MediaCreator class uses an empty Sitecore.Resources.Media.MediaCreatorOptions instance. In order for the file to be uploaded to a specified location in the Media Library, the Destination property on the Sitecore.Resources.Media.MediaCreatorOptions instance must be set, or it will be uploaded directly to the Media Library root.
Here’s the good news: the FileCreated() method is declared virtual, so why not subclass it and then override this method to include some custom logic to set the Destination property on the MediaCreatorOptions instance?
I did just that in the following class:
using System.IO; using Sitecore.Configuration; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.IO; using Sitecore.Pipelines.GetMediaCreatorOptions; using Sitecore.Resources.Media; namespace Sitecore.Sandbox.Resources.Media { public class MediaCreator : Sitecore.Resources.Media.MediaCreator { private string UploadLocation { get; set; } public override void FileCreated(string filePath) { Assert.ArgumentNotNullOrEmpty(filePath, "filePath"); if (string.IsNullOrWhiteSpace(UploadLocation)) { base.FileCreated(filePath); return; } SetContext(); lock (FileUtil.GetFileLock(filePath)) { string destination = GetMediaItemDestination(filePath); if (FileUtil.IsFolder(filePath)) { MediaCreatorOptions options = MediaCreatorOptions.Empty; options.Destination = destination; options.Build(GetMediaCreatorOptionsArgs.FileBasedContext); this.CreateFromFolder(filePath, options); } else { MediaCreatorOptions options = MediaCreatorOptions.Empty; options.Destination = destination; long length = new FileInfo(filePath).Length; options.FileBased = (length > Settings.Media.MaxSizeInDatabase) || Settings.Media.UploadAsFiles; options.Build(GetMediaCreatorOptionsArgs.FileBasedContext); this.CreateFromFile(filePath, options); } } } protected virtual void SetContext() { if (Context.Site == null) { Context.SetActiveSite("shell"); } } protected virtual string GetMediaItemDestination(string filePath) { if(string.IsNullOrWhiteSpace(UploadLocation)) { return null; } string fileNameNoExtension = Path.GetFileNameWithoutExtension(filePath); string itemName = ItemUtil.ProposeValidItemName(fileNameNoExtension); return string.Format("{0}/{1}", UploadLocation, itemName); } } }
The UploadLocation property in the class above is to be defined in Sitecore Configuration — see the patch include configuration file below — and then populated via the Sitecore Configuration Factory when the class is instantiated (yes, I’m defining this class in Sitecore Configuration as well).
Most of the logic in the FileCreated() method above comes from its base class Sitecore.Resources.Media.MediaCreator. I had to copy and paste most of this code from its base class’ FileCreated() method as I couldn’t just delegate to the base class’ FileCreated() method — I needed to set the Destination property on the MediaCreatorOptions instance.
The Destination property on the MediaCreatorOptions instance is being set to be the UploadLocation plus the Media Library Item name — I determine this full path in the GetMediaItemDestination() method.
Unfortunately, I also had to bring in the SetContext() method from the base class since it’s declared private — this method is needed in the FileCreated() method to ensure we have a context site defined.
Now, we need a way to set an instance of the above in the Creator property on the Sitecore.Sandbox.Resources.Media.MediaProvider instance. Unfortunately, there was no easy way to do this without having to subclass the Sitecore.Sandbox.Resources.Media.MediaProvider class, and then set the Creator property via its constructor:
using Sitecore.Configuration; namespace Sitecore.Sandbox.Resources.Media { public class MediaProvider : Sitecore.Resources.Media.MediaProvider { private MediaCreator MediaCreator { get; set; } public MediaProvider() { OverrideMediaCreator(); } protected virtual void OverrideMediaCreator() { Sitecore.Resources.Media.MediaCreator mediaCreator = GetMediaCreator(); if (mediaCreator == null) { return; } Creator = mediaCreator; } protected virtual Sitecore.Resources.Media.MediaCreator GetMediaCreator() { return Factory.CreateObject("mediaLibrary/mediaCreator", false) as MediaCreator; } } }
The OverrideMediaCreator() method above tries to get an instance of a Sitecore.Resources.Media.MediaCreator using the Sitecore Configuration Factory — it delegates to the GetMediaCreator() method to get this instance — and then set it on the Creator property of its base class if the MediaCreator obtained from the GetMediaCreator() method isn’t null.
If it is null, it just exits out — there is a default instance created in the Sitecore.Resources.Media.MediaCreator base class, so that one would be used instead.
I then replaced the “out of the box” Sitecore.Resources.Media.MediaProvider with the new one above, and also defined the MediaCreator above in the following patch include configuration file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <mediaLibrary> <mediaProvider patch:instead="mediaProvider[@type='Sitecore.Resources.Media.MediaProvider, Sitecore.Kernel']" type="Sitecore.Sandbox.Resources.Media.MediaProvider, Sitecore.Sandbox" /> <mediaCreator type="Sitecore.Sandbox.Resources.Media.MediaCreator, Sitecore.Sandbox"> <UploadLocation>/sitecore/media library/uploaded</UploadLocation> </mediaCreator> </mediaLibrary> </sitecore> </configuration>
Let’s see how we did.
As you can see, I have an empty uploaded Media Library folder:
Let’s move an image into the /upload folder of my Sitecore instance:
After reloading the “uploaded” Media Library folder, I see that the image was uploaded to it:
I would also to like to mention that this solution will also work if there is no uploaded folder in the Media Library — it will be created during upload process.
If you have any thoughts on this, please share in a comment.
Nice, good override. If you create the folder structure on the file system then it will be replicated in the media library, i.e. create `/upload/uploadedimage.jpg` and it will be uploaded to that folder in the media library and the folder structure automatically created.