Microsoft provide a CDN service on the cloud. This service needs to take the static files from a local folder on the same website. I'm going to show you how to add to Sitecore the feature to clone all the files of the Media Library into this folder. This action is going to happen just when you publish those items from the Master database to Web database. Also, you will have to accomplish some extra configurations on the Content Delivery servers to take them from the CDN servers.
I'm going to show you and I'm going to provide you a package to use Azure CDN with your Sitecore 7.* project hosted in Azure.
This package is already on the Sitecore Marketplace. Also, the source code is on Github too.
Step 1 - Create the CDN service on Azure.
Got to the App Services section. Press on the CDN option. Then press on the Quick Create button. On the Origin Domain dropdown select the URL of your website. (Note: CDN is going to search the images on a CDN folder in the root of your website). After the creation of the CDN service, I'll recommend to enable HTTPS on it.
Step 2 - Install the package and config it
You have to install the package on the site that you have selected on the dropdown when you create the Azure CDN service.
After the installation, you will see a new file in the folder /App_Config/Includes. The file is called SitecoreFromArg.CdnAzure.config and it has 2 parameters inside:
Enabled: This parameter allow you to disable this feature.
<Enabled>yes</Enabled>
SourceDomain: You can use this parameter to change the name of the default folder where Azure is looking for the static files.
<SourceDomain>/cdn</SourceDomain>
Step 3 - Republish media items
Go to Content Editor on the server where you have installed the package, and republish all the items of the Media Library folder.
Final step - Change the configurations on the Content Delivery servers
Set the parameter Media.AlwaysIncludeServerUrl in true:
<setting name="Media.AlwaysIncludeServerUrl" value="true" />
Add the following string in the parameter Media.MediaLinkPrefix:
<setting name="Media.MediaLinkPrefix" value="~/"/>
Add the CDN Domain on the parameter Media.MediaLinkServerUrl (this parameter isn't on Sitecore 6.*):
<setting name="Media.MediaLinkServerUrl" value="http://aabbccdd.vo.msecnd.net"/>
Finally, remove the attribute value="ashx" from the parameter Media.RequestExtension:
<setting name="Media.RequestExtension" />
There is the code of my package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Sitecore.Data.Items; | |
using Sitecore.Diagnostics; | |
using Sitecore.Jobs; | |
using Sitecore.Publishing.Pipelines.PublishItem; | |
namespace SitecoreFromArg.CdnAzure | |
{ | |
public class CdnPublishing : PublishItemProcessor | |
{ | |
public string Enabled { get; set; } | |
public string SourceDomain { get; set; } | |
public override void Process(PublishItemContext context) | |
{ | |
if (Enabled.ToLower() != "yes") return; | |
Log.Debug("Performing CDN validations", this); | |
Assert.ArgumentNotNull(context, "context"); | |
var contextItem = context.PublishHelper.GetSourceItem(context.ItemId); | |
if (contextItem == null) return; | |
if (!contextItem.Paths.IsMediaItem) return; | |
MediaItem mediaItem = contextItem; | |
if (string.IsNullOrEmpty(mediaItem.Extension)) return; | |
var mediaStream = mediaItem.GetMediaStream(); | |
if (mediaStream == null || mediaStream.Length == 0) return; | |
var processor = new CdnAzureProcessor(SourceDomain); | |
Log.Debug("Starting CDN synchonization", this); | |
try | |
{ | |
Sitecore.Context.Job.Status.State = JobState.Initializing; | |
if (context.Action == Sitecore.Publishing.PublishAction.None) | |
processor.UploadFileOnCdn(mediaItem); | |
if ((context.Action == Sitecore.Publishing.PublishAction.PublishSharedFields) || | |
(context.Action == Sitecore.Publishing.PublishAction.PublishVersion)) | |
processor.ReplaceFileOnCdn(mediaItem); | |
if (context.Action == Sitecore.Publishing.PublishAction.DeleteTargetItem) | |
processor.DeleteFileOnCdn(mediaItem); | |
Sitecore.Context.Job.Status.State = JobState.Finished; | |
} | |
catch (Exception ex) | |
{ | |
var cdnEx = new Exception(string.Format("CDN Processing failed for {1} ({0}). {2}", contextItem.ID, contextItem.Name, ex.Message)); | |
Log.Error(cdnEx.Message, cdnEx, this); | |
context.Job.Status.Failed = true; | |
context.Job.Status.Messages.Add(cdnEx.Message); | |
} | |
Log.Debug("CDN synchronization finished", this); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> | |
<sitecore> | |
<pipelines> | |
<publishItem> | |
<processor type="SitecoreFromArg.CdnAzure.CdnPublishing,SitecoreFromArg.CdnAzure" | |
patch:before="processor[@type='Sitecore.Publishing.Pipelines.PublishItem.RemoveUnknownChildren, Sitecore.Kernel']"> | |
<Enabled>yes</Enabled> | |
<SourceDomain>/cdn</SourceDomain> | |
</processor> | |
</publishItem> | |
</pipelines> | |
</sitecore> | |
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.IO; | |
using System.Text; | |
using Sitecore; | |
using Sitecore.Data.Items; | |
using Sitecore.Diagnostics; | |
using System.Web; | |
namespace SitecoreFromArg.CdnAzure | |
{ | |
public class CdnAzureProcessor | |
{ | |
public string CdnSourceDomain { get; set; } | |
public CdnAzureProcessor(string cdnSourceDomain) | |
{ | |
var cdnFolderPath = GetCdnFolderPath(cdnSourceDomain); | |
if (!Directory.Exists(cdnFolderPath)) Directory.CreateDirectory(cdnFolderPath); | |
CdnSourceDomain = cdnSourceDomain; | |
} | |
public void UploadFileOnCdn(MediaItem mediaItem) | |
{ | |
var filePath = GetFilePath(mediaItem); | |
var mediaStream = mediaItem.GetMediaStream(); | |
var exist = File.Exists(filePath); | |
if (exist) | |
{ | |
ReplaceFileOnCdn(mediaItem); | |
} | |
else | |
{ | |
var directoryPath = Path.GetDirectoryName(filePath); | |
if (directoryPath == null) return; | |
Directory.CreateDirectory(directoryPath); | |
using (var fileStream = File.Create(filePath)) | |
{ | |
mediaStream.CopyTo(fileStream); | |
} | |
Log.Debug(string.Format(@"CDN has added: {0}", filePath), this); | |
} | |
} | |
public void ReplaceFileOnCdn(MediaItem mediaItem) | |
{ | |
var filePath = GetFilePath(mediaItem); | |
var exist = File.Exists(filePath); | |
if (exist) | |
{ | |
DeleteFileOnCdn(mediaItem); | |
UploadFileOnCdn(mediaItem); | |
} | |
else | |
{ | |
UploadFileOnCdn(mediaItem); | |
} | |
} | |
public void DeleteFileOnCdn(MediaItem mediaItem) | |
{ | |
var filePath = GetFilePath(mediaItem); | |
var exist = File.Exists(filePath); | |
if (exist) | |
{ | |
File.Delete(filePath); | |
} | |
Log.Debug(string.Format(@"CDN has deleted: {0}", filePath), this); | |
} | |
[NotNull] | |
private string GetFilePath(MediaItem mediaItem) | |
{ | |
var builder = new StringBuilder(); | |
builder.Append(HttpRuntime.AppDomainAppPath.Remove(HttpRuntime.AppDomainAppPath.Length - 1)); | |
builder.Append(CdnSourceDomain.Replace('/', '\\')); | |
builder.Append(mediaItem.MediaPath.Replace('/', '\\')); | |
builder.Append("."); | |
builder.Append(mediaItem.Extension); | |
return builder.ToString(); | |
} | |
[NotNull] | |
private string GetCdnFolderPath(string cdnSourceDomain) | |
{ | |
var builder = new StringBuilder(); | |
builder.Append(HttpRuntime.AppDomainAppPath.Remove(HttpRuntime.AppDomainAppPath.Length - 1)); | |
builder.Append(cdnSourceDomain.Replace('/', '\\')); | |
return builder.ToString(); | |
} | |
} | |
} |
Hello Lautaro,
ReplyDeleteThank you for the information..
I have a query. What I have understood is this module will create a folder structure based on your media library structure on the publish event of media. It will not upload the media to the Azure storage account.Do you know how this can be achieved because media files will be fetched from the Azure Storage account only.
Please correct me if I am wrong.
Hi Alok,
DeleteThat folder is enough. You don't need to upload them to Azure Storage. When you create the CDN service, the wizard will ask you about the website where you want to apply this service. In this moment CDN will start listening the files you create on this special folder. You can test it easily adding an static file to this folder manually.
I'm not sure if I was clear on my explanation. Please ask me any other question you have.
We are unable to connect to the content delivery network (CDN) that delivers the video stream? and how can you fix it?
ReplyDeleteglobal server load balancing
Video streaming and static files are not addressed in the same way for the CDNs. The main idea behind the CDN is the same one, but each service is implementing it technically in a different way. You have to ask this question to the technical support of the service you have selected. Also, the infrastructure approach you are using could impact on this issue as well. Apologies, I cannot help you.
DeleteWhat is SSL?is this important to have it when u order web hosting?
ReplyDeletewhat is cdn
Glad you post a question on my blog, but I'm not seeing a relationship between this question and CDN, Azure or Sitecore.
DeleteGoing back to your question, here you have an excellent explanation about SSL (https://en.wikipedia.org/wiki/Transport_Layer_Security)
This feature is important if you want to encrypt the communication between the hosting and the users.