Friday, January 1, 2016

What’s new in SharePoint 2016 Remote API Part 3 (Files)

Technorati Tags: ,

This is part three of a blog series about the new features available in SharePoint 2016 Beta 2 Remote API. This blog post will talk about the new features for files. Unfortunately, some of the new methods available for files do not necessarily work in Beta 2 for reasons I don’t understand as of yet.

File Versions

Getting previous version binaries in SharePoint has always been a pain. CSOM methods really did not work. Many developers used File.OpenBinaryDirect method along with the URL to the previous version which looked something like this: http://yoursite/yoursubsite/_vti_history/512/Documents/Book1.xls

Of course you knew that 512 means version 1.0 or the magic number 1025 means version 2.1. There was a fomula that you were required to use to generate these numbers and construct the URL. However, even if you knew how to constuct the URL the CSOM OpenBinaryDirect method would return a 404. Most developers just used the .Net web client and the URL to get the binary. Now SharePoint 2016 has added OpenBinaryStream method on the SPFileVersion class.

The OpenBinaryStream method is available for both CSOM, REST and JSOM. The following is a code example using JSOM. Unfortunately, this still has the problem of decoding the binary stream similar to the issue of getting the binary for a SPFile object pointed out in Mikael Svenson blog post How to copy files between sites. If the file is not a text file then you get a file that has all the pages but the pages are blank. This problem still exists in SharePoint 2016. So I recommend using this method only with managed CSOM. Also, you still need to know the magic number for the version to retrieve it. The SPFileVersions collection has many methods that use the label, except the method to retrieive it. I can delete by the label but not retrieve by the label. Why?

function getVersionBinarySP() {
    var dfd = $.Deferred();
    var binaryData;
    hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
    appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
    fileContentUrl = appweburl + "/_api/SP.AppContextSite(@target)/web/GetFileByServerRelativeUrl('/sites/seconddev/testdups/testdoc0.pdf')/Versions/GetById(512)/OpenBinaryStream?@target='" + hostweburl + "'";

    var executor = new SP.RequestExecutor(appweburl);
    var info = {
        url: fileContentUrl,
        method: "GET",
        binaryStringResponseBody: true,
        success: function (data) {
            binaryData = data.body;
            dfd.resolve(binaryData);
        },
        error: function (err) {
            dfd.reject(err);
        }
    };
    executor.executeAsync(info);
    return dfd;
}

What’s new for Files?

There a many new methods and properties available for remotely accessing SharePoint files. Some of these have already been implemented in SharePoint Online. Below is a list of the new methods.

StartUpload, ContinueUpload, ContinueUpload, FinishUpload Methods

These methods are for uploading files in fragments (chunking) which is useful for large files when connections can be dropped or throttled. SharePoint Online has had these methods for a while to help developers with throttling. The methods are enabled for CSOM, REST and JSOM. Once again I would not try using these from JavaScript given the issues with stream encoding. I did test these with managed CSOM, below is some code:

public void UploadFile()
{
    ClientContext clientContext = new ClientContext("http://win2012r2dev/sites/seconddev");

    var documentsFolder = clientContext.Web.GetFolderByServerRelativeUrl("/sites/seconddev/testdups");
    Microsoft.SharePoint.Client.File uploadFile = documentsFolder.Files.GetByUrl("testdoc12.pdf");
           
    clientContext.Load(uploadFile);
    clientContext.ExecuteQuery();

    using (var fs = System.IO.File.OpenRead(@"c:\wp8_enterprise_device_management_protocol.pdf"))
    {
        byte[] bytes = new byte[fs.Length];
        fs.Read(bytes, 0, (int)fs.Length);
        using (var inputStream = new MemoryStream())
        {
            inputStream.Write(bytes, 0, bytes.Length);
            inputStream.Position = 0;

            //Set up size of fragments to upload.
            int chunkSize = 1000000;
            int index = 0;

            Int64 offset = 0;
            var myGuid = Guid.NewGuid();

            while (inputStream.Position < inputStream.Length)
            {
                byte[] buffer = new byte[chunkSize];
                int chunkBytesRead = 0;
                while (chunkBytesRead < chunkSize)
                {
                    int bytesRead = inputStream.Read(buffer, chunkBytesRead, chunkSize - chunkBytesRead);
                    MemoryStream stream = new MemoryStream();
                    stream.Write(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        break;
                    }
                    chunkBytesRead += bytesRead;
                    if (index == 0)
                    {
                        offset = uploadFile.StartUpload(myGuid, stream).Value;
                        clientContext.ExecuteQuery();
                    }
                    else if (inputStream.Position == inputStream.Length)
                    {
                        uploadFile.FinishUpload(myGuid, offset, stream);
                        clientContext.ExecuteQuery();
                    }
                    else
                    {
                        offset = uploadFile.ContinueUpload(myGuid, offset, stream).Value;
                        clientContext.ExecuteQuery();

                    }
                }

                index++;
            }

        }
    }

}
These methods all work together to help upload files in fragments. You just need to make sure to use the same GUID when sending the request. Unfortunately, I could not get this to work. I keep getting a Cobalt (File Syncronization) error of “Invalid Argument”. However, please try the code I could be missing something.
ExecuteCobaltRequest Method

Once again this is a method for editing files that are supported by the Office Online Server (WOPI Protocol) that has been available in Office 365. This method is now supported for SharePoint 2016 and Office Online Server Preview. There is little documentation on this method, The method is supported in managed CSOM and JSOM. The method takes a stream as an argument so probably not a good candidate for JavaScript.

GetImagePreviewUrl

This method returns a URL to the new image preview handler that has been used by Delve in Office 365. I blogged about using this in your own hosted Add-In or search templates Get Faster Search Previews in SharePoint Online. Well now a handy method will build if for you in SharePoint 2016. This will work with office, pdf, tiff, bmp and png files. You can pass in the width and height and it will calculate a resolution and send back a URL similar to this:

http://win2012r2dev/sites/SecondDev/_layouts/15/getpreview.ashx?path=/sites/SecondDev/testdups/testdoc0.pdf&resolution=Width300&clienttype=webapp 

Unfortunately, the getpreview.ashx handler code will not work unless it is running on SharePoint Online. Huh?

GetPreAuthorizedAccessUrl

This method returns a URL to download the document. The method takes an integer as an argument representing the number of hours the link is good for. It has a guest token attached in the querystring. Below is an example of what is returned. You will have to log in with the userid listed in the querystring.

http://win2012r2dev/sites/SecondDev/_layouts/15/download.aspx?guestaccesstoken=NduByOVhPo1QJyW0FEZVaikciOnDC03opCSiUWylH4s%3d&docid=0bfef46a04c964c7e983248c2051709fa&expiration=12%2f31%2f2015+6%3a11%3a57+AM&userid=1&authurl=True

GetWOPIFrameUrl

This is another convenience method. This is a URL to navigate to an office file (including PDF) in Office Online Server Preview. The method takes one argument an integer representing the SPWOPIFrameAction enumeration. This method supports, View, Edit, InteractivePreview, and MobileView. Below is an example of what is returned.

http://win2012r2dev/sites/SecondDev/_layouts/15/WopiFrame.aspx?sourcedoc={bec083e0-6ba6-4b87-9937-5f3c488e2f8a}&action=interactivepreview

Update

The SPFile and SPFolder now support property bags via the remote API. So you can now save metadata to your file and folders using the new Properties property along with this Update method.

function updateFilePropertyBag() {
    appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
    hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
    context = new SP.ClientContext(appweburl);
    appContextSite = new SP.AppContextSite(context, hostweburl);

    targetWeb = appContextSite.get_web();
    var file = targetWeb.getFileByServerRelativeUrl("/sites/seconddev/testdups/testdoc0.pdf")
    var properties = file.get_properties();
    
    context.load(file);
    context.load(properties);

    context.executeQueryAsync(function () {
        var p = properties;
        p.set_item("whatever", false);
        file.update();

        context.executeQueryAsync(function () { },
            function (sender, args) {
                alert(args.get_message() + '\n' + args.get_stackTrace());
        });

    }, function (sender, args) {
        alert(args.get_message() + '\n' + args.get_stackTrace());
    });

}

Information Rights Management is a high priority in SharePoint 2016

Below is a list of new properties exposed on the SPFile in th remote API. As you can see Information Rights Management is a high priority with the surfacing of two new properties InformationRightsManagementSettings and EffectiveInformationRightsManagement. The former is the default settings and the later is what actually is set for the document if IRM is enabled. These settings are stored in the SPFile property bag but these new properties make it easy to read the right property. This comes in handy if your developing an application and you want to make sure you can print or view a document.

SharePoint 2016 Remote API Progress but not Perfection

Well this post shows you that the remote API for files is getting better in SharePoint 2016, but problems remain. The current Beta 2 remote API seems to be ahead of the what has been implemented on the server side. Some features appear in the API but are not fully implemented or may never be implemented. If some of the methods are for O365 only then they should be removed. The merging of the API between O365 and SharePoint 2016 may cause problems for developers since it will be impossible to tell which method works in what platform. There is more information to come. I urge you to start using the remote API more so you can take advantage of new features when your customers need them and to avoid the ones that do not work.

1 comment:

Unknown said...

Thanks for your posts, really helpful.
Do you know why GetFilebyID can return 404 (not found) on the fail, that I know it's metadata and unique id, so seems that file is there but REST call returns 404 and Getfilebyserverrelativeurl returns that lengths of the request is too long. All this for Sharepoint Online.
Thanks

Post a Comment