Viewing Files Outside of SharePoint with IFileRetriever

With Vizit 6.0 comes the ability to write and register custom file retrievers which allow you to view documents from nearly any content source imaginable. This article walks you through the process of developing and registering a custom file retriever.

Getting Started

If you haven't already, take a look at this article and follow the instructions in it to get started writing your custom code:

The rest of this article will explain the details of how to write the specific code for viewing files outside of SharePoint.

The Basics

Vizit 6.0 contains a new assembly named Vizit.API.Interfaces. For custom file retrievers, you must implement the IFileRetriever interface:

bool CanRetrieveFile(Uri uri);

Stream GetStream(Uri uri);

String GetExtension(Uri uri);

String GetCacheKey(Uri uri);

string Id { get; }

Implementing the IFileRetriever Interface

When loading documents in Vizit, an initial attempt is made to determine the source of the file. In many cases, the file resides in SharePoint so we use our file retrieving mechanism for these files. Up until now, if the file did not reside in SharePoint, a simple anonymous web request was made to retrieve the file. The CanRetrieveFile method allows for your IFileRetriever to be chosen as the appropriate retriever. If no IFileRetrievers are flagged as handling a particular Uri, we attempt to retrieve the file anonymously over the web. Here is an example CanRetrieveFile implementation that marks this IFileRetriever as handling any files residing on the "testserver" using a URL starting with "file://testserver" or a properly escaped UNC path (ie: \\\\testserver\\path\\to\\file.ext):

public bool CanRetrieveFile(Uri uri) 
return uri.Scheme == "file" && uri.Host == "testserver";

Before the file is retrieved, we must determine the format of the file. Vizit uses a file extension to dictate which decoder will be used to render the file. Many techniques can be used to determine the appropriate format of a file, but a simple approach would be to capture the extension off the end of the file's URL:

public String GetExtension(Uri uri) 
return Path.GetExtension(uri.ToString());

In other scenarios, it may be necessary to retrieve the stream to determine the proper extension. In those cases, it is highly recommended that you maintain a private variable to store the stream after you've used it. This will allow for any subsequent calls to get the stream to be fast.

Next, we need to retrieve the stream. In our case, we are simply calling File.Open on the provided path as the Application Pool user:

public Stream GetStream(Uri uri)
Stream s = null;
SPSecurity.RunWithElevatedPrivileges(() => {
s = File.Open(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read);
return s;

To avoid unnecessary calls to acquire a stream, Vizit caches the result of a call to GetStream based on a cache key that you provide. If the cache key changes, Vizit will request a new stream from the IFileRetriever. Here is an implementation of GetCacheKey that combines the provided path with information about the last time the file was modified:

public string GetCacheKey(Uri uri)
string cacheKey = "";
SPSecurity.RunWithElevatedPriviledges(() => {
cacheKey = uri.ToString() + File.GetLastWriteTime(uri.LocalPath).Ticks;

Lastly, you must implement the getter of the Id property. The value of this may not change. It is the unique value identifying this plugin and can be used to unregister it in the future.

public string Id { get { return "Contoso.TestServerFileRetriever"; } }

Implications on Permissions and Access

Because the IFileRetriever controls the immediate connection to the file source, and because calls to IFileRetriever are made in the context of the web request, you have full access to SPContext and can base your access to various files on that information. This will allow for trusted access to nearly any third party source. With the advent of Claims in SharePoint 2010 and 2013, even more secure mechanisms can be used to gain access to content without the need to impersonate a common, high-access user account as shown in the example above.

Have more questions? Submit a request


Article is closed for comments.
Powered by Zendesk