+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

Sitefinity Streaming API: Overview

by Dilyan Rusev
This is a part of the blog post series that explain the new streaming API for Sitefinity 3.7 SP3. You can view the TOC in the first blog post.
In my previous blog post, I introduced the problem and explained our approach towards fixing it. Now, I am going to introduce the changes to the data layer.
New Classes and Interfaces
All classes and interfaces in this section are in Telerik.Cms.Engine.Streaming (Telerik.Cms.Engine.dll)
IStreamableContent (inherits IContent)
Extends IContent by providing streaming capabilities
  • string StreamingProviderName { get; set; }
    Name of the streaming provider used to upload the IContent's content

    This acts the same ways as IContent.ProviderName, but stores the streaming provider name used for streaming the binary content of IContent.
    You should never set this property unless you are absolutely certain it won't break the functionality.

  • IStreamingContentProvider StreamingProvider { get; }
    Retrieves an instance of the streaming provider used to upload the
    IContent's content

    This is just a helper method to ease the retrieval of the streaming provider as an instance instead of just a name.
The default implementation of IContent in Sitefinity (the Nolics one) already implements IStreamableContent, so you need to implement it only in your custom providers if you need it.
DownloadStream (inherits System.IO.Stream, it is an abstract class)
Represents a stream that can be only used for reading

You should inherit from this class for your own custom streaming providers. This is where the bulk of the work gets done.
UploadStream (inherits System.IO.Stream, it is an abstract class)
Represents a write-only stream.

You should inherit from this class for your own custom streaming providers. This is where you would typically create new content. Again, most of the work is done here and in DownloadStream.
IStreamingContentProvider (does not inherit anything)
Marks a provider as a provider that can stream binary content through streams
  • DownloadStream GetDownloadStream(IStreamableContent content)
    Get a stream that can be used for downloading the binary content of an item
    content: Content item whose binary content we want to download using streaming
    returns: Stream specialized in reading.
  • UploadStream GetUploadStream(IStreamableContent content)
    Get a stream that can be used for uploading the binary content of an item
    content: Content item whose binary content we want to upload using streaming
    returns: Stream specialized in writing.
  • void DeleteContent(IStreamableContent content)
    Delete the binary data associated with content
    content
    is a content item that has its binary data available for streaming.

    You should call this in your custom providers whenever an IContent is deleted, should your providers support streaming. Sitefinity already does this for its default implementation of IContent (the Nolics implementation). 
IChunkData (does not inherit anything)
Provides information about a separate chunk of data. Use this to feed a stream, for example.
  • Guid ContentID { get; set; }
    ID of the content of whose binary data this chunk is a part of
  • byte[] Data { get; set; }
    Binary data of the chunk
  • int Ordinal { get; set; }
    Ordinal position of the chunk in the sequence of chunks that make up the whole binary data of the content item
  • long BytesUsed { get; set; }
    Specifies how much of the bytes in the chunk are actually used
IChunkInfo (inherits  IEnumerable<IChunkData>)
Provides information about binary information that is transmitted in chunks of smaller data

To retrieve the binary data, enumerate through the chunk info to get chunks individually.
Avoid casting to finite types (array, list, etc.), as this will cause all chunks to be loaded
into memory. Doing so might require a lot of memory and will defeat the purpose of using
chunks in the first place.
  • Guid ContentID { get; }
    ID of the content whose binary data is represented in chunks
  • int NumberOfChunks { get; set; }
    Total number of data chunks
  • long TotalSize { get; set; }
    Total size, in bytes, of all chunks
  • long ChunkSize { get; set; }
    Chunk size, as uploaded
  • IChunkData GetChunk(int ordinal)
    Read a chunk of binary data by its ordinal
    ordinal is the position of the chunk in the sequence of binary chunks
    Returns: Queried chunk from data base or null.
By looking at the two interfaces, IChunkData and IChunkInfo, you can see that at no time we load the whole binary data in memory. In the case of Nolics, we split data in chunks, and then wrap those chunks in two stream classes. For the file system provider, we don't need chunks, as API for working with the file system in .NET already uses chunks, so we need to simply wrap them.
IChunkContentProvider (inherits nothing)
Marks a provider that can handle binary content in chunks
  • IChunkInfo CreateChunkInfo(Guid contentID)
    Create a chunk info by attaching it to an IContent item with contentID
    contentID is ID of the IContent object to attach to.
    Returns: Newly created chunk info.
  • IChunkInfo CreateChunkInfo(IContent contentToAttachTo)
    Same as above, but uses an IContent instance instead
  • IChunkInfo GetChunkInfo(Guid contentID)
    Get a chunk info by the ID of the IContent it is attached to
    contentID is ID of the content
    Returns IChunkInfo or null if nothing is attached to the content with contentID.
  • IChunkInfo GetChunkInfo(IContent content)
    Same as above; uses IContent instance instead.
  • IChunkInfo QueryChunkInfo(IContent content)
    Same as the GetChunkInfo overloads, but loads as a query, as opposed to loading by primary key. If primary key is not find (in some cases), an exception will be thrown. A query will always return null if nothing is found.
  • IChunkInfo QueryChunkInfo(Guid contentID)
    Same as above, but uses an ID instead of an IContent instance.
  • void DeleteChunkInfo(IChunkInfo chunkInfoToDelete)
    Delete a chunk info along with all other chunk data associated with the same IContent
    chunkInfoToDelete is the IChunkInfo to delete

    You should rarely need to call this manually, as IContent should take care of deleting its IChunkInfo and IChunkData objects when it itself is deleted.
  • void SaveChunkInfo(IChunkInfo chunkToSave)
    Save changes made to a chunk info
  • IChunkData CreateChunkData(Guid contentID, int ordinal)
    Create a chunk data by associating it with a content and ordinal
    contentID is IContent ID to associate with
    ordinal is position in the chunk sequence.
    Returns: newly created IChunkData

    Call this method only if you are absolutely certain what you do. It is better to use streaming providers and let Sitefinity handle the complexity of handling chunks of data while presenting you with a standard .NET System.IO.Stream to work with.
  • IChunkData CreateChunkData(IContent contentToAttachTo, int ordinal)
    Same as above, but uses an instance of IContent instead of an IContent's ID

    Call this method only if you are absolutely certain what you do. It is better to use streaming providers and let Sitefinity handle the complexity of handling chunks of data while presenting you with a standard .NET System.IO.Stream to work with.
  • IChunkData GetChunkData(IContent contentAttachedTo, int ordinal)
    Get a chunk data from a sequence of chunks associated with a content item
    contentAttachedTo is an IContent the desired chunk data is associated with
    ordinal is position in the chunk data sequence
    Returns: instance looked up by primary key

    Call this method only if you are absolutely certain what you do. It is better to use streaming providers and let Sitefinity handle the complexity of handling chunks of data while presenting you with a standard .NET System.IO.Stream to work with.
  • IChunkData GetChunkData(Guid contentID, int ordinal)
    Same as above, but uses ID instead of an IContent instance
  • IChunkData QueryChunkData(IContent contentAttachedTo, int ordinal)
    Same as the GetChunkData overrides, but uses query lookup instead of primary key lookup
  • IChunkData QueryChunkData(Guid contentID, int ordinal)
    Same as above, but uses ID instead of an IContent instance
  • void DeleteChunkData(IChunkData dataToDelete)
    Delete a chunk data
    dataToDelete is the chunk data to delete

    Call this method only if you are absolutely certain what you do. It is better to use streaming providers and let Sitefinity handle the complexity of handling chunks of data while presenting you with a standard .NET System.IO.Stream to work with.
  • void SaveChunkData(IChunkData dataToSave)
    Save a specified dataToSave
    dataToSave is the data chunk to save.

    Call this method only if you are absolutely certain what you do. It is better to use streaming providers and let Sitefinity handle the complexity of handling chunks of data while presenting you with a standard .NET System.IO.Stream to work with.
  • long ChunkSize { get; set; }
    Gets or sets the size of the chunk.

    Set this in your custom stream during upload and use it as a reference when downloading, as this is customizable and it might have changed.
The Nolics implementation of IStreamingContentProvider implements IChunkContentProvider as well. As you can see, the binary data is saved, not only read, in chunks. IChunkContentProvider does not inherit IStreamingContentProvider, because this is not a 1:1 relationship. A streaming provider might be a chunk provider, but a chunk provider (your custom implementation, probably) might not be a streaming provider. This is done to give you freedom should you decide to use the chunk API separately for your own projects.
And, at last, here is the last new class:

StreamHelper (does not inherit anything, it is a static class)
  • public static void CopyStream(Stream source, Stream target, bool resetSource, ProviderBase provider)
    Helper method that will copy efficiently the entire stream. This overload optimizes chunk size if provider supports chunks.
    source is the stream to copy from
    target is the stream to copy to
    provider is the provider to get chunk size from if it supports chunks.
    resetSource will be true to reset the source position to its original value before the copying

    This method will throw ArgumentNullException when source or target is null.
  • public static void CopyStream(Stream source, Stream target, bool resetSource, int chunkSize)
    This overload lets us specify the chunk size.
  • public static void CopyStream(Stream source, Stream target, bool resetSource)
    This overload will choose a reasonable default for chunk size.
  • public static byte[] ReadToEnd(Stream source, bool resetSource)
    Reads a stream into memory and returns a byte array. Use with caution! Will load the whole array in memory
    source is the stream to read into memory
    resetSource tells whether to reset source after reading to its original position.
    Returns a byte array containing the stream's binary data.

    Throws ArgumentNullException when source is null.
  • public static bool IsStreamingCapable(IContent content, bool respectSettings, out IStreamingContentProvider streamingProvider)
    Determines whether a content item is capable of streaming.
    content is the content item whose capabilities we want to determine.
    respectSettings determines whether to respect the enable/disable setting in web.config.
    streamingProvider will returns a streaming provider for the content item
    Returns true or false, depending on the content item capabilities.

    Throws ArgumentNullException if content is null.
Changes to old classes will be discussed in my next blog post.

Leave a comment