Implementing IStorageProvider in Document Viewer API using C#

Hi,

We would like to use GroupDocs.Viewer to display documents stored in our proprietary repository. It looks like the IStorageProvider interface is the way to achieve this, but the documentation is a bit sparse.

There is a sample mentioned in this thread http://groupdocs.com/Community/forums/3724/sharepoint-storageprovider/showthread.aspx#3724 but the DropBox link is no longer valid, could you provide it again?

Also, the following information would be very helpful:
- What is the minimum set of methods we need to implement to get a “read-only” storage provider with no browsing (i.e. “Open” button is disabled in client)? What is the sequence of calls?
- Is there a way to call Viewer.SetStorageProvider to provide a custom implementation for retrieving documents from the repository, but use the default filesystem implementation for the cache/temporary folder? This should reduce the amount we need to implement I think.

Thanks

Phil

Hello Phil,

Thank you for your interest in GroupDocs.Viewer for .NET. Yes, it supports custom storage provider, which should implement the “IStorageProvider” interface.

1. Here is the link for the updated solution. Sample project is “GroupDocsViewerMVCDemo”, it demonstrates, how to use custom storage provider. In the “Application_Start” method of the Global.asax you can see, how the custom storage provider is specified. Two instances of custom storage provider are created: first for the documents and second for the cache. The custom storage provider is declared in the “CustomStorageProvider.cs” file - it actually is a wrapper around “AzureFileStorage” class. “AzureFileStorage” constructor, in turn, obtains the MS Azure credentials. When you will download this project, you’ll see standard credentials for the Azure Emulator; if it is installed on your machine, you can launch the project without modifying it. Otherwise, if you don’t have Azure Emulator installed on your machine, you will obtain this exception.

2. All methods in the “IStorageProvider” interface are mandatory, except two: “public Stream CreateFile(string path)” and “public FileSystemEntity[] ListEntities(string path)”. First one is responsible for uploading a new document to the server, and second - for obtaining a list of documents on the server. If you don’t want to implement “ListEntities” method, you should also disable a “Open file” button in the toolbar.

3. Yes, this is possible. Basically, when you don’t use custom or remote storage provider, GroupDocs.Viewer uses own “Groupdocs.Storage.LocalFileStorage” provider. This class is public, so you also can use it. It doesn’t implement the “IStorageProvider” interface, so you need to create a wrapper for the “Groupdocs.Storage.LocalFileStorage”, which should implement “IStorageProvider”. And then specify this wrapper as the second parameter in the “Viewer.SetStorageProvider” static method.

If you will have more questions please feel free to contact us.

Thanks, that should prove useful. However, there was a miscommunication between two of our teams as to whether the back-end repository was actually ready, so we will be going with the standard folder-based storage in the short term, and it will be a couple of months before we start to use IStorageProvider

Hello Phil, thanks for answering, we are glad that our post was helpful. If you have some additional questions about custom storage provider or anything else, please feel free to contact us.

@denisgvardionov Hi Denis,

I am needing to implement a similar solution as to what was required by Phil. It appears your link to the updated solution posted on Jan 15 is no longer valid. Can you post a new link to a sample .NET MVC project that uses “IStorageProvider” to encrypt cached files?

If you have any other examples of GroupDocs being implemented in such a way that all cached files are encrypted I would be interested in seeing those as well. Our implementation doesn’t have too many particular requirements, other than all cached files need to be encrypted.

Best,

Seth

@DocuManager9000

Welcome to our forum!

Back then, GroupDocs.Viewer had UI and backend modules but now it is a stand-alone .NET library that is focused on rendering documents.

The UI part is now open-source and can be found in GroupDocs.Total-Angular repository. You can also check our demo projects at GitHub. All of the demo projects are using a local disk to read files and to write cache.

Meanwhile, we’ll try to find the solution that was posted by Denis.

Hi Vladimir,

Thanks for your response. I think we are misunderstanding each other a little bit though. I have pulled down the demo projects and inspected them. I understand that all of the demo project us a local disk to read and to write cache. However, many of them do this behind the “wall” of your compiled .dll files. Can you point me to a demo that writes and reads all of it’s cache using an encryption standard? From what I understand that example posted by Denis was using the IStorageProvider interface to do this.

Best,

Seth

@DocuManager9000

Thank you for sharing the details. Please check if this sample_app.zip (153.2 KB) will fit your requirements. The Startup.cs file contains SecureFileCache class and two not implemented methods DecryptEntry and EncryptEntry that you can implement using an encryption standard. The sample app was built based on the ASP.NET Core demo. Please let us know if this what you were looking for.

Vladimir,

Thank you for the response! When I click on that link I get a blank webpage with the message: “Sorry, this file is private. Only visible to topic owner and staff members.”

Best,

Seth

@DocuManager9000

Please try downloading the same file from Google Drive.

Thank you Vladimir for your help, this is perfect! Much appreciated!

1 Like

@DocuManager9000

You’re welcome!

Feel free to contact us if you have more questions!

If I implement the SecureFileCache class in the GroupDocs.Viewer MVC project, how will this affect the Viewer class? If I am not mistaken, the View() method of the Viewer class creates the .png views of each page? Will this not bypass the encryption methods of the SecureFileCache?

@DocuManager9000

It won’t affect the Viewer class behavior in any way.

Right, the Viewer class creates HTML, PNG, or JPEG pages.

Please check the following simplified interaction flow

The first run:

  1. File selected by a user
  2. File read from disk - LocalFileStorage is responsible for this
  3. File rendered using GroupDocs.Viewer
  4. Output pages stored on disk - LocalFileCache is responsible here
  5. Output pages rendered

The second run:

  1. File selected by a user
  2. Output pages read from disk - LocalFileCache
  3. Output pages rendered

When using SecureFileCache as shown in the sample app the SecureFileCache is just placed between the third and the fourth steps

First Run

  1. File selected by a user
  2. File read from disk - LocalFileStorage is responsible for this
  3. File rendered using GroupDocs.Viewer
  4. SecureFileCache encodes output pages
  5. Output pages stored on disk - LocalFileCache is responsible here
  6. Output pages rendered

Second Run

  1. File selected by a user
  2. Output pages read from disk - LocalFileCache
  3. SecureFileCache decodes output pages
  4. Output pages rendered

So, as you can see SecureFileCache and GroupDocs.Viewer do not interact with each other directly and do not have any effect on each other.

Vladimir, thank you for the detailed response. This response makes sense in the context of the ASP.NET Core demo’s implementation. I am struggling because the LocalFileStorage methods are not exposed in the ASP.NET Core demo. As such, with the example given, you have solved my needs for encrypting the cache, but a route to encrypting the LocalFileStorage is not clear.

The .MVC product demo does expose the methods that manage LocalFileStorage. However it does not easily allow for encryption of the LocalFileCache. I have been attempting to encrypt the local cache of the .MVC demo by mirroring the SecureFileCache class of the Core demo. You give the simplified interaction of the Core demo as follows:

  1. File selected by a user
  2. File read from disk - LocalFileStorage is responsible for this
  3. File rendered using GroupDocs.Viewer
  4. SecureFileCache encodes output pages
  5. Output pages stored on disk - LocalFileCache is responsible here
  6. Output pages rendered

However, in the .MVC demo this seems to be slightly different:

  1. File selected by a user
  2. File read from disk - LocalFileStorage is responsible for this
  3. File rendered using GroupDocs.Viewer View() method. This method does not return the rendered file Stream or byte array. Instead it appears the View() method immediately writes the rendered file views to disc.

This leaves me in the situation where the Core demo allows for easy encryption of the file cache, but does not seem to allow me to encrypt the file storage. The .MVC demo meanwhile exposes the file storage method such that I can encrypt the stored files, however since the View() method immediately writes rendered files to disc, I cannot implement the SecureFileCache class in the same way that I would be able to in the Core demo.

Hopefully this makes sense, apologies if I am not explaining well. Do you have a demo that exposes the methods for both file caching and file storage, such that encryption can more easily be implemented?

@DocuManager9000

You can encrypt/decrypt input files by registering your implementation of IFileStorage that is available as a public interface. Let me post the code snippet:

services.AddTransient<IFileStorage>(_ =>
    new SecureFileStorage(
        new LocalFileStorage("./Files")));

// Implementation

public class SecureFileStorage : IFileStorage
{
    private readonly IFileStorage _fileStorage;

    public SecureFileStorage(IFileStorage fileStorage)
    {
        _fileStorage = fileStorage;
    }

    public Task<IEnumerable<FileSystemEntry>> ListDirsAndFilesAsync(string dirPath)
    {
        return _fileStorage.ListDirsAndFilesAsync(dirPath);
    }

    public async Task<byte[]> ReadFileAsync(string filePath)
    {
        var bytes = await _fileStorage.ReadFileAsync(filePath);

        // Decrypt file here

        return bytes;
    }

    public Task<string> WriteFileAsync(string fileName, byte[] bytes, bool rewrite)
    {
        // Encrypt file here

        return _fileStorage.WriteFileAsync(fileName, bytes, rewrite);
    }
}

The complete sample application can be downloaded at Google Drive.

In case you would like to take a look at the source code of LocalFileStorage and LocalFileCache can be found in GroupDocs.Viewer-for-.NET-UI repository.

Thank you, this is extremely helpful. I now have encrypted file storage working perfectly and am close to completing encryption of the file cache.

I know this question borders on asking how to write code in general, so for this I apologize, and I sincerely hope this is my last question for a while :slight_smile:

I am struggling with returning values from the DecryptEntry(TEntry entry) and EncryptEntry(TEntry entry) methods. This method allows for the single argument ‘entry’ to be passed in, and it may be of many types. The types we are interested in handling and returning are byte[] and Stream. Your code shows how to easily cast a var of type TEntry as byte[] or Stream, but how do I return a value from the function of type byte[] or Stream?

It appears that I cannot implicitly or explicitly cast TEntry entry as a byte[] or Stream. I also cannot go to the definition of TEntry as it appears to be obfuscated in the IFileCache dll. Google also does not return helpful results for TEntry, and results for TValue don’t seem related. See the below screenshot for clarification:

image.png (51.5 KB)

@DocuManager9000

You can try casting the result byte array or stream to object first and then to TEntry

TEntry entry = (TEntry) ((object) bytes);

Let us know if it works for you.

This nested cast works for me! Thank you.

@DocuManager9000

You’re welcome!