We are running Episerver in a Load balanced enviroment, we have 3 nodes.
1 Node is dedicated to the CMS (so www.website.com/episerver)
then the other two nodes are dedicated to the public facing website (so www.website.com)
I have currently added some functionality that generates a PDF file during the Publishevent when logged in to episerver. This fuuncotality is working correctly but the issue I have is.....
becuase its a load balanced enviroment the generated PDF is not on the other nodes, so the link to it on the website fails.
My first instinict to solve this problem is to contact the people that manage our servers and ask them to set up replication on the folder the PDFs are saved too, but for reasons I won't bore you with, we can't actually do that.
So....... I was thinking, currently when a bit of media file is uploaded via the CMS, we don't need to do anything special to make the media available on the public facing website, so I can assume that replication for the media is already set up.Instead of generated the PDF and writing it to the disk, I was thinking if I programaticly create a media entry in to Episerver it should be replicated and work in our Load balanced enviroment with out having to make any changes to the servers.Is this possible? If so can someone help me out with some code samples of how to create a new media item?Any other ideas of how I can solve this issue?
Checkout the IBlobFactory, this allows you to upload and programtically work with the storage that Episerver uses for it's assets. How it stores these changes depending on the environment, for example locally it will be in a folder and on the DXC it will use Azure Blob Storage.
Here's an except of a file upload for an excel file I've build as an admin plugin
public ActionResult PostFile(HttpPostedFileBase file)
if (file.ContentLength > 0)
var fileName = Path.GetFileName(file.FileName);
var extension = Path.GetExtension(fileName);
if (extension == Extension)
var guid = new Guid.NewGuid();
var container = Blob.GetContainerIdentifier(guid);
var blob = _blobFactory.CreateBlob(container, ".xlsx");
In this I've changed the guid to be created but you can make this a constant or store this somewhere like the DDS if you need to working with these items after. We usually map DDS records for our specific usuages to the items we've uploaded and then use them to reload from the IBlobFactory
var uri = new Uri(record.FileId);
var blob = _blobFactory.GetBlob(uri);
In this example record.FileId is a string, this record when save in the DDS record comes from the blob.ID when using the above code to save the blob.
This sounds very promissing and the kind of thing I was looking for.
The only thing I think I still need to understand is how would I for example create a folder structure in the media section...............so for example.......... Genreated Pdfs > Sub Folder > Pdf File.... how can I do this programicly?
If you want this actually viewable in the media library that's actually a different approach. You'd need to instead make sure you have a PDF document type configured for storing in the asset library
[ContentType(GUID = "93A9152A-4FB8-48C1-8FA5-115B138F8437")]
[MediaDescriptor(ExtensionString = "pdf")]
public class Pdf : MediaData
Then use the standard IContentRepository to dynamically create folders and files that area actualy IContent types. This will under the covers store the items the same way in the blob library
You can use the IContentRepository to
If service is your instance of IContentRepository and parentContentReference is the folder. If you create the folders the same way with the OOTB folder type you can create the folders from the a top level parent folder or the root of the assets folder.
You can after calling the code above set all the properties and call
Service.Save(writableVersion, SaveAction.Publish, AccessLevel.NoAccess)
if writeableVersion is your item. It's basically the same for any IContent in episerver, although you'll have to set the Blob file programtically using similar to the above code I posted
That is exactly what I needed, this is my first Episever project so I am still finding my way around. - thank you, will mark as the answer when I have it all working.
I've found some code that does something very similar and downloads images from a URL to upload them in the CMS. Hopefully this can help too
This code is using image but they are just blobs so should work the same. Also to note the .CreateWriteableClone() this is only needed when updating an existing item as when you load an item it's readonly. If it's new items would can just use the .GetDefault
var imageUrl = new Uri(_siteConfigurationResolver.SiteSettings.ArtlogicCmsUrl.Uri, imagePath);
var client = new HttpClient();
var response = client.GetAsync(imageUrl).Result;
existingCmsImage = existingCmsImage?.CreateWritableClone() as ImageFile;
//Get a new empty file data
var cmsImage = existingCmsImage ??
cmsImage.Name = filename;
cmsImage.DisplayName = Path.GetFileName(imagePath);
cmsImage.AlternativeText = imageCaption;
//Create a blob in the binary container
var blob = _blobFactory.CreateBlob(cmsImage.BinaryDataContainer, Path.GetExtension(imagePath));
using (var imageStream = response.Content.ReadAsStreamAsync())
//Assign to file and publish changes
cmsImage.BinaryData = blob;
var newCmsImageId = _contentRepository.Save(cmsImage, SaveAction.Publish);
This is how I am creating a new folder, which works, but how do I check if the folder already exists? I only want to create one if it dosen't already exist, I have noticed episerver allows you to have folders with the same name, is there a way to turn that off? How would I find the folder again after its created?
public ContentReference CreateFolder(string folderName)
var contentFile = contentRepository.GetDefault<ContentFolder>(SiteDefinition.Current.GlobalAssetsRoot);
contentFile.Name = folderName;
return contentRepository.Save(contentFile, SaveAction.Publish, AccessLevel.NoAccess);
Call the contentRepository GetChildren<> method with the SiteDefinition.Current.GlobalAssetsRoot to get the children then you can check the collection returned for the folder that matches the name.
Scott Read - you are a super star, got it all working now. Thank you, just going to implement the folder thing so I don't create a new folder each time, and then I am done! thank you again, would have taken aggggessss without your code samples.
No worries, glad I could help :-)