Running Episerver In a Load balanced environment.

Ayo
Ayo
Vote:
 

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?

#190819
Apr 17, 2018 14:28
Vote:
 

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);

        if (!string.IsNullOrWhiteSpace(fileName))
        {
            var extension = Path.GetExtension(fileName);

            if (extension == Extension)
            {
                DeleteFile();

                var guid = new Guid.NewGuid();
                var container = Blob.GetContainerIdentifier(guid);
                var blob = _blobFactory.CreateBlob(container, ".xlsx");
                blob.Write(file.InputStream);
            }
        }
    }
}

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.

#190825
Edited, Apr 17, 2018 14:55
Ayo
Vote:
 

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? 

#190833
Apr 17, 2018 15:17
Vote:
 

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

Service.GetDefault<Pdf>(parentContentReference)

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

#190835
Apr 17, 2018 15:27
Ayo
Vote:
 

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.

#190836
Apr 17, 2018 15:31
Vote:
 

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;
            response.EnsureSuccessStatusCode();

            existingCmsImage = existingCmsImage?.CreateWritableClone() as ImageFile;
            
            //Get a new empty file data
            var cmsImage = existingCmsImage ??
                          _contentRepository.GetDefault<ImageFile>(alphabetFolder.ContentLink);
            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())
            {
                blob.Write(imageStream.Result);
            }

            //Assign to file and publish changes
            cmsImage.BinaryData = blob;
            var newCmsImageId = _contentRepository.Save(cmsImage, SaveAction.Publish);
#190842
Edited, Apr 17, 2018 15:57
Ayo
Vote:
 

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);
        }
#190845
Apr 17, 2018 16:11
Vote:
 

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. 

#190846
Apr 17, 2018 16:14
Ayo
Vote:
 

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.

#190860
Apr 17, 2018 17:39
Vote:
 

No worries, glad I could help :-)

#190878
Apr 18, 2018 9:08
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.