CSOM. Upload document

Sample how to upload documents to SharePoint document library with SharePoint .NET client object model CSOM (Client-Side Object Model).

Example project is a simple Console Application created in Visual Studio 2017. This application can be able to work with most of existing SharePoint versions:

  • SharePoint 2013
  • SharePoint 2016
  • SharePoint 2019
  • SharePoint Online

Even SharePoint 2010 is not a problem. I just have no developer instance of this version of SharePoint for testing.

Console Application

Run Visual Studio and add new Console App (.NET Framework):

After application is created we need to add necessary references to work with SharePoint.

There are at least two ways to add the reference to the project:

Add link to existing Microsoft.SharePoint.Client.dll assembly located in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\{15|16}\ISAPI folder (if you have installed SharePoint on-premise instance).

The second way is adding special nuget-package released by Microsoft.

In this sample project I use nuget.

Nuget Package

To install Microsoft.SharePointOnline.CSOM package which contains both SharePoint and Project CSOM libraries just run the following command in Package Manager console:


PM> Install-Package Microsoft.SharePointOnline.CSOM

All necessary references added and now we can add some value.

ClientContext

To interact with SharePoint via CSOM we need to initialize ClientContext


// SharePoint Site URL
var siteUrl = "https://sharepointsiteurl";

using (var ctx = new ClientContext(siteUrl))
{
    // Working with SharePoint Data
}

Before performing any CSOM operation you need to provide credentials.

ClientContext Credentials

Credentials property must be presented with class implementingICredential interface:

There are many such classes:

  • System.Net.CredentialCache
  • System.Net.NetworkCredential
  • Microsoft.SharePoint.Client.SharePointOnlineCredentials
  • Microsoft.SharePoint.IdentityModel.OAuth2.OAuth2BearerCredentials

Use SharePointOnlineCredentials for SharePoint Online and NetworkCredential in other case.

If you'll try to use SharePointOnlineCredentials to interact with on-premise SharePoint the following exception will be thrown:

Microsoft.SharePoint.Client.ClientRequestException: The IDCRL response header from server '{SharePointSiteUrl}' is not valid. The response header value is 'NTLM'. The response status code is 'Unauthorized'. All response headers are 'SPRequestDuration=3, SPIisLatency=0, MicrosoftSharePointTeamServices=16.0.0.10711: 1; RequireReadOnly, Content-Length=16, Content-Type=text/plain; charset=utf-8, Date=Fri, 27 Jul 2018 14:53:07 GMT, Server=Microsoft-IIS/10.0, WWW-Authenticate=NTLM, X-Powered-By=nosniff'.

Or something like the following:

System.NotSupportedException: Cannot contact web site '{SharePointSiteUrl}' or the web site does not support SharePoint Online credentials. The response status code is 'OK'. The response headers are
X-SharePointHealthScore=4,
SPRequestGuid=74ee7e9e-8c75-309f-b9c2-2db7076e60a4,
request-id=74ee7e9e-8c75-309f-b9c2-2db7076e60a4,
X-FRAME-OPTIONS=SAMEORIGIN,
SPRequestDuration=460,
SPIisLatency=0,
MicrosoftSharePointTeamServices=16.0.0.4327,
X-AspNet-Version=4.0.30319,
X-Powered-By=ASP.NET

In the sample app if SharePointOnlineCredentials does not work (it means we work with on-premise SharePoint) Credentials replaced with NetworkCredential:


// SharePoint Site URL
var siteUrl = "https://sharepointsiteurl";
// Login
var userLogin = "userLogin@domain";
// Password
var userPassword = "P@ssw0rd";

var pwd = new SecureString();
foreach (var c in userPassword) pwd.AppendChar(c);
var SPOCredentials = new SharePointOnlineCredentials(userLogin, pwd);
var SPCredentials = new NetworkCredential(userLogin, pwd);

using (var ctx = new ClientContext(siteUrl))
{
    try
    {
        // try to use SharePoint Online Credentials
        ctx.Credentials = SPOCredentials;
        ctx.ExecuteQuery();
    }
    catch (ClientRequestException)
    {
        // switch to NetworkCredential
        ctx.Credentials = SPCredentials;
        ctx.ExecuteQuery();
    }
    catch (NotSupportedException)
    {
        // switch to NetworkCredential
        ctx.Credentials = SPCredentials;
        ctx.ExecuteQuery();
        Console.WriteLine("SharePoint On-premise");
    }

    // Working with SharePoint Data
}

For the sample app it does not matter what the version of SharePoint (or Project Online) is on other side.

Document

Document for uploading generated in runtime. It's just a text file:


var fileBytes = new byte[] { };

using (var ms = new MemoryStream())
{
    using (TextWriter tw = new StreamWriter(ms, Encoding.UTF8))
    {
        for (var i = 0; i < 100; i++)
        {
            tw.WriteLine(@"The quick brown fox jumps over the lazy dog");
        }
        fileBytes = ms.ToArray();
    }
}

Upload Document

For uploading we need to provide an object of type FileCreationInformation:


// Information about the file
var fileInformation = new FileCreationInformation
{
    // Server relative url of the document
    Url = library.RootFolder.ServerRelativeUrl + "/fileName2.txt",
    // Overwrite file if it's already exist
    Overwrite = true,
    // Content of the file
    Content = fileBytes
};
// Upload the file to root folder of the Document library
library.RootFolder.Files.Add(fileInformation);

ctx.ExecuteQuery();

It was first approach. And it has a limitation: maximum file size is 2MB. If file size is greater than 2MB CSOM raises an exception:

Microsoft.SharePoint.Client.ServerException: 'The request message is too big. The server does not allow messages larger than 2097152 bytes.'

For this case there is another approach to upload a document.

Upload Large Document

For large file uploading there is a SaveBinaryDirect method of Microsoft.SharePoint.Client.File class:


Microsoft.SharePoint.Client.File.SaveBinaryDirect(
    //Client Context
    ctx,
    // Server relative url of the document
    library.RootFolder.ServerRelativeUrl + "/fileName2.txt",
    // Content of the file
    new MemoryStream(fileBytes),
    // Overwrite file if it's already exist
    true);
ctx.ExecuteQuery();

Even your document is extremely large there is an approach in CSOM.

Chunked Upload Document

If your file is large or you just want to upload a document by chunk there is a set of methods in Microsoft.SharePoint.Client.File class:

  • StartUpload
  • ContinueUpload
  • FinishUpload
  • CancelUpload

We start uploading using StartUpload method, then continue uploading with ContinueUpload method and complete uploading with FinishUpload. If you do not want to finish uploading - call CancelUpload method.

First of all we create empty file and only after that start uploading the content:


// Create empty chunked file
var fileChunkedInformation = new FileCreationInformation
{
    // Server relative url of the document
    Url = library.RootFolder.ServerRelativeUrl + "/fileName3.txt",
    // Overwrite file if it's already exist
    Overwrite = true,
    Content = new byte[] {}
};
// Upload the file to root folder of the Document library
var fileChunked = library.RootFolder.Files.Add(fileChunkedInformation);

ctx.ExecuteQuery();

// Upload Id
var uploadId = Guid.NewGuid();

// Offset of the byte array (file content)
var fileBytesOffset = 0;

// Offset of the uploaded file
long fileChunkedOffset = 0;

// Size of a chunk
var fileChunkArrayBuffer = new byte[1024];

while (fileBytesOffset < fileBytes.Length)
{
    // How many bytes will be read from file content
    var length = Math.Min(fileChunkArrayBuffer.Length, fileBytes.Length - fileBytesOffset);
                    
    // Copy bytes to buffer array
    Buffer.BlockCopy(fileBytes, fileBytesOffset, fileChunkArrayBuffer, 0, length);

    // Buffer array to stream
    using (var ms = new MemoryStream(fileChunkArrayBuffer))
    {
        // If the offset equals 0 - Start Upload
        if (fileChunkedOffset == 0)
        {
            var fileChunkedOffsetResult = fileChunked.StartUpload(uploadId, ms);
            ctx.ExecuteQuery();
            fileChunkedOffset = fileChunkedOffsetResult.Value;
        }
        // Continue uploading
        else if (fileBytesOffset + length <= fileBytes.Length)
        {
            var fileChunkedOffsetResult = fileChunked.ContinueUpload(uploadId, fileChunkedOffset, ms);
            ctx.ExecuteQuery();
            fileChunkedOffset = fileChunkedOffsetResult.Value;
        }
        // Last chunk- Finish uploading
        else
        {
            fileChunked.FinishUpload(uploadId, fileChunkedOffset, ms);
            ctx.ExecuteQuery();
        }
    }

    fileBytesOffset += length;
}

And the result:

Source Code

Source code located here: https://code.msdn.microsoft.com/Upload-document-to-32056dbf.

Links

Complete basic operations using SharePoint client library code


Share

Comments