Today I created a small tool in Episerver that reads through my local file system and programmatically uploads all media files and folders in the same structure as they are in the file system. As of this writing, Episerver does not currently support uploading of folders and subdirectories.
I have developed the tool to show within the Episerver > CMS > Admin > Tools section and looks like this
The destination field lets users choose to upload to either the GlobalAssetFolder or to a SiteAssetsFolder.
When it finishes the upload, the file system that looks like this:
Will then be transferred including all subdirectories and files to the media asset pane:
So how did I achieve this? It’s pretty simple!
First of all, update your web.config to let your application know that you have a publicmodule.
<episerver.shell>
<publicModules rootPath="~/modules/" autoDiscovery="Modules">
<add name="ImportMediaAssets" />
</publicModules>
</episerver.shell>
Then I created the following controller under inside the modules folder.
[GuiPlugIn(
Area = PlugInArea.AdminMenu,
Url = "/ImportMediaAssets",
DisplayName = "Import media assets")]
public class ImportMediaAssetsController : Controller
{
// Insert your logic here...}
Notes regarding the above code:
- GuiPlugin – attribute that allows you to extend the Episerver user-interface.
- GuiPlugin Area – tells the Episerver user-interface where to put your plugin
- GuiPlugin Url – If you’re using MVC, then this will be the controller name + action name (empty if Index)
Then, in my controller, I have two action methods. The Index() gets called on click of the “Import media assets” link in the Tools menu. While the BeginUpload() gets called on submit of the form, just like hooking up any other MVC post.
public ActionResult Index()
{
var model = new ImportMediaAssetModel();
return View(model);
}
public ActionResult BeginUpload(ImportMediaAssetModel postedModel)
{
var directory = new DirectoryInfo(postedModel.SourcePath);
if (postedModel.Destination == ImportMediaAssetModel.GlobalAssetFolder)
{
CreateAndUploadFilesAndFolders(directory, ContentReference.SiteBlockFolder, postedModel.OverrideExistingItems);
}
return View(directory);
}
The important function here is the CreateUploadFilesAndFolders() method.
- Iterate through each subdirectory and programmatically create in Episerver
- Iterate through each file and call the UploadFile() method which we will look at later
private void CreateAndUploadFilesAndFolders(DirectoryInfo dir, ContentReference parentLink, bool overrideFiles)
{
foreach (var childFile in dir.GetFiles())
{
if (fileCount >= 1) break;
UploadFile(childFile, parentLink, overrideFiles);
}
foreach (var childDir in dir.GetDirectories())
{
var storedFolder = _contentRepository.Service.GetBySegment(parentLink, childDir.Name,
LanguageSelector.AutoDetect()) as ContentFolder;
if (storedFolder != null)
{
CreateAndUploadFilesAndFolders(childDir, storedFolder.ContentLink, overrideFiles);
}
else
{
var contentFolder = _contentRepository.Service.GetDefault<ContentAssetFolder>(parentLink);
contentFolder.Name = childDir.Name;
var contentAssetsFolderReference = _contentRepository.Service.Save(contentFolder, SaveAction.Publish);
CreateAndUploadFilesAndFolders(childDir, contentAssetsFolderReference, overrideFiles);
}
}
}
Finally, here’s my UploadFile() implementation.
private void UploadFile(FileInfo file, ContentReference parentLink, bool overrideFiles)
{
//Get a suitable MediaData type from extension
var extension = GetExtension(file.Name);
var mediaType = _contentMediaResolver.Service.GetFirstMatching(extension);
var contentType = _contentTypeRepository.Service.Load(mediaType);
// Try get existing media
var storedMedia = _contentRepository.Service.GetBySegment(parentLink, file.Name,
LanguageSelector.AutoDetect()) as IContentMedia;
if (!overrideFiles && storedMedia != null) return;
//Get a new empty file data
var media = _contentRepository.Service.GetDefault<IContentMedia>(parentLink, contentType.ID);
var container = media.BinaryDataContainer;
var blob = _blobFactory.Service.CreateBlob(container, extension);
using (var fs = new FileStream(file.FullName, FileMode.Open))
{
blob.Write(fs);
}
media.BinaryData = blob;
media.Name = file.Name;
_contentRepository.Service.Save(media, SaveAction.Publish, AccessLevel.NoAccess);
}
Of course you have to also implement the razor view, that will be completely up to you. But my model looks like this:
public class ImportMediaAssetModel
{
public string Destination { get; set; }
public string SourcePath { get; set; }
public bool OverrideExistingItems { get; set; }
}
Easy right? 🙂
If you have any questions, please let me know!