Try our conversational search powered by Generative AI!

Johan Björnfot
Dec 3, 2013
  25710
(8 votes)

Working with Media programmatically

Prior to version 7.5, files in EPiServer CMS (as exposed in the old file manager) were handled by a VPP (Virtual Path Providers) system. In the latest version of EPiServer a new media file system with a new user interface is introduced. Content stored in this system is referred to as “media”. This system is based on the same content (IContent implementations) system used for blocks and pages, as well as products from the catalog in Commerce. This means that files shares same support for routing, custom properties, typed models, custom renderings, security etc. as other content types.

To work programmatically with media is much like working with other content types (e.g. pages). For example are the CRUD operations done through IContentRepository. In this post I will go through some of the interfaces/base classes used for media and give some examples on how to programmatically work with media.

 

IContentMedia

The requirement for a media type is that it implements IContentMedia which is defined as:

    public interface IContentMedia : IContent, IBinaryStorable
    {
        Blob Thumbnail { get; set; }
        string MimeType { get; }
    }

We can see that IContentMedia extends IContent and also IBinaryStorable which is defined as:

    public interface IBinaryStorable
    {
        Blob BinaryData { get; set;}
        Uri BinaryDataContainer { get; }
    }

The Property BinaryData of type Blob (see class below) is used to handle the actual binary content of the media. The property BinaryDataContainer is the Uri for the container containing the Blob. All binaries for a content instance (including versions and languages) are stored in a common container. The definition of Blob looks like (I have omitted the static methods):

public class Blob
{
        public Uri ID { get; }
        public virtual Stream OpenRead();
        public virtual Stream OpenWrite();
        public virtual void Write(Stream data);
}

So a blob instance basically contains stream based Read and Write operations. The actual Blob instance is delivered by the configured BlobProvider. The default blob provider stores the binary data on disk (there is a subfolder blobs in the folder specified by attribute basePath on configuration element appData). The NuGet packages EPiServer.Azure and EPiServer.Amazon contains provider to store binary data in Azure Blob storage respectively Amazon S3.

 

Defining a media type

A media type is defined by having a class implementing IContentMedia and decorated with ContentTypeAttibute. The attribute MediaDescriptorAttribute can be used to map which extensions a certain type handles. There are some base classes MediaData, ImageData and VideoData that can be used to specify media types. So for example a project can have types defined as:

    [ContentType(GUID = "EE3BD195-7CB0-4756-AB5F-E5E223CD9820")]
    public class GenericMedia : MediaData
    {
        public virtual String Description { get; set; }
    }

    [ContentType(GUID = "0A89E464-56D4-449F-AEA8-2BF774AB8730")]
    [MediaDescriptor(ExtensionString = "jpg,jpeg,jpe,ico,gif,bmp,png")]
    public class ImageFile : ImageData
    {
        public virtual string Copyright { get; set; }
    }

    [ContentType(GUID = "85468104-E06F-47E5-A317-FC9B83D3CBA6")]
    [MediaDescriptor(ExtensionString = "flv,mp4,webm")]
    public class VideoFile : VideoData
    {
        public virtual string Copyright { get; set; }
        [UIHint(UIHint.Image)]
        public virtual ContentReference PreviewImage { get; set; }
    }

 

Creating a new media instance

Below is an example on how to create a new media instance. Here the media item is created under SiteDefinition.Current.GlobalAssetRoot which is the folder for assets that is shared between sites (this is the root for the media gadget). To structure media instances of EPiServer.Core.ContentFolder can be created.

var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
var blobFactory = ServiceLocator.Current.GetInstance<BlobFactory>();

//Get a new empty file data
var file1 = contentRepository.GetDefault<GenericFile>(SiteDefinition.Current.GlobalAssetsRoot);
file1.Name = "Readme.txt";
            
//Create a blob in the binary container
var blob = blobFactory.CreateBlob(file1.BinaryDataContainer, ".txt");
using (var s = blob.OpenWrite())
{
     StreamWriter w = new StreamWriter(s);
     w.WriteLine("Hello world");
     w.Flush();
}

//Assign to file and publish changes
file1.BinaryData = blob;
var file1ID = contentRepository.Save(file1, SaveAction.Publish);

 

Get media type from extension

There is a ContentMediaResolver that can be used to resolve which content type to use for a certain extension. See below:

var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
var mediaDataResolver = ServiceLocator.Current.GetInstance<ContentMediaResolver>();

//Get a suitable MediaData type from extension
var mediaType = mediaDataResolver.GetFirstMatching(".txt");
var contentType = contentTypeRepository.Load(mediaType);
//Get a new empty file data
var media = contentRepository.GetDefault<IContentMedia>(SiteDefinition.Current.GlobalAssetsRoot, contentType.ID);

 

Replace the binary data for a media item

When a binary data is to be replaced for a media item a new Blob instance should be created. That is you should not call OpenWrite on the existing blob instance. The reason for this is to support versioning so that if an editor reverts back to an old version the binary data should follow. Normally you should not need to bother about deleting Blobs, there is a scheduled jobs that takes care of deleting unused Blobs. Below is an example on how to replace the binary data for a media in a new version:

var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
var blobFactory = ServiceLocator.Current.GetInstance<BlobFactory>();

//the file to update, not hardcoded of course
var fileID = new ContentReference(444);

//Get the file
var file1 = contentRepository.Get<GenericFile>(fileID).CreateWritableClone() as GenericFile;

//Create new blob
file1.BinaryData = blobFactory.CreateBlob(file1.BinaryDataContainer, ".txt");
using (var s = file1.BinaryData.OpenWrite())
{
    StreamWriter w = new StreamWriter(s);
    w.WriteLine("Hello world");
    w.Flush();
}

//publish new version
var file1ID = contentRepository.Save(file1, SaveAction.Publish);

Working with content assets

In previous versions it was possible to have a local VPP folder for a content where files related to that content could be stored. Now with media based assets a content instance that implements EPiServer.Core.IResourcable can have related assets (media, blocks etc.) stored in an instance of EPiServer.Core.ContentAssetFolder. Resources stored as content assets are to be seen as exclusive assets for that content instance and hence the resources are not selectable from other content instances.

The method GetOrCreateAssetFolder on class EPiServer.Core.ContentAssetHelper can be used to get the asset folder for a content item.

Dec 03, 2013

Comments

Joshua Folkerts
Joshua Folkerts Dec 3, 2013 07:51 PM

Nice Post Johan, Much needed that is for sure. Keep them coming!

Johan Kronberg
Johan Kronberg Dec 4, 2013 11:46 AM

Can't wait to work with this!

Dec 5, 2013 09:35 AM

Great summary.

Please login to comment.
Latest blogs
Solving the mystery of high memory usage

Sometimes, my work is easy, the problem could be resolved with one look (when I’m lucky enough to look at where it needs to be looked, just like th...

Quan Mai | Apr 22, 2024 | Syndicated blog

Search & Navigation reporting improvements

From version 16.1.0 there are some updates on the statistics pages: Add pagination to search phrase list Allows choosing a custom date range to get...

Phong | Apr 22, 2024

Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog