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 implementing ICredential interface:

There are some 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 '' 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 '' 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.NETv

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

Vitaly Zhukov

Vitaly Zhukov

SharePoint Architect, Developer, Technical Trainer, Microsoft MVP (Office Development). Above 15 years of total experience working with SharePoint, Dynamics CRM, Office 365, and Microsoft stack development.

You May Also Like

Linq to Sharepoint. Part 1

Linq to Sharepoint. Part 1

Linq to SharePoint. Part 2

Linq to SharePoint. Part 2

Linq to SharePoint. Part 3

Linq to SharePoint. Part 3

Linq to SharePoint. Part 4

Linq to SharePoint. Part 4

Linq to SharePoint. Part 5. Choice and MultiChoice fields

Linq to SharePoint. Part 5. Choice and MultiChoice fields