CSOM is able to upload large files

When you search for custom solutions to upload files to SharePoint, you will probably be advised to use REST API offer by SharePoint 2013, which is fine. The reason why people advise you so is mainly because CSOM is not able to upload files larger than 2Mb. Which is partial true. There is a solution to upload large files using CSOM and I will show you below how to do it.

Solutions I choose is based on Silverlight. I know you will tell me it is obsolete. But for the moment is still on the market, available at least till 2021 and is sometimes a good solution. So I see no reasons why you cannot use it. So what I achieved looks like below and if you like what you see, continue to read. 🙂
Silverlight Uploader

Solution markup

Solution contains a textbox, where selected file name will be display, a label to display error or success message, a button to open file dialog window to choose the file you want to upload and another button to start upload process. Without too many comments, here is the markup.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" x:Class="HPFileUploader.MainPage"
    mc:Ignorable="d" Height="113" Width="800">
    <toolkit:TwilightBlueTheme  HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top">
        <Grid x:Name="LayoutRoot" Background="White" Margin="0,4,0,0" Height="113" VerticalAlignment="Top" Width="800">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="13*"  />
                <ColumnDefinition Width="787*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <Button Content="Choose" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" Margin="225,10,0,0" VerticalAlignment="Top" Width="75" x:Name="btnUpload" Click="btnUpload_Click" RenderTransformOrigin="0.52,-0.864"/>
            <TextBox  Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" IsReadOnly="True"  x:Name="txtFileName" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="220" Grid.ColumnSpan="2"/>
            <Button Grid.Column="1" Grid.Row="0"   Content="Start Upload" HorizontalAlignment="Left" Margin="305,10,0,0" VerticalAlignment="Top" Width="92" x:Name="startUpload" Click="startUpload_Click"/>

            <sdk:Label HorizontalAlignment="Left" x:Name="lblError" VerticalAlignment="Center" VerticalContentAlignment="Center" Height="28" Margin="0,11,0,17" Grid.Row="1"  Width="680" Grid.ColumnSpan="2">

            </sdk:Label>
      </Grid>
    </toolkit:TwilightBlueTheme>
</UserControl>

Add SharePoint client reference

If you choose to create a standalone Silverlight project, you will need to add some libraries and references to your project. The easiest way it to you use NuGet and add Microsoft SharePoint Foundation 2010 Client Object Model package.
FileUploader - Manage NuGet Packages

Once you add the package you need to be sure the following references are set.
2015-01-28 22_00_52-HPFileUploader - Microsoft Visual Studio (Administrator)

Code behind

And here is essence of this article.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.SharePoint.Client;
using System.IO;
using System.Text;
using System.Xml.Linq;
using System.Windows.Browser;


namespace FileUploader
{
    public partial class MainPage : UserControl
    {

        private FileStream fs;
        private string fName = "";

        public MainPage()
        {
            InitializeComponent();
        }

        /// <summary>
        /// This method is handling click even for choose file button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUpload_Click(object sender, RoutedEventArgs e)
        {

            this.lblError.Content = "";            

            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Multiselect = false;

            if (dlg.ShowDialog() == true)
            {
                fs = dlg.File.OpenRead();
                fName = dlg.File.Name.ToString();
                this.txtFileName.Text = fName;
            }
            else
            {
                fs = null;
                fName = "";
                this.txtFileName.Text = "";
            }
        }

        /// <summary>
        /// Start upload button click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void startUpload_Click(object sender, RoutedEventArgs e)
        {
            if (this.fs == null || this.fName == "") return;

            using (ClientContext ctx = new ClientContext("https://sharepoint"))
            {

                this.btnUpload.IsEnabled = false;
                this.startUpload.IsEnabled = false;
                this.startUpload.Content = "Wait...";

                Web web = ctx.Site.RootWeb;
                List oList = web.Lists.GetByTitle("Attachments");
                FileCreationInformation fci = new FileCreationInformation();
                fci.ContentStream = fs;
                fci.Overwrite = true;
                fci.Url = "https://sharepoint/Attachments/" + fName;
                Microsoft.SharePoint.Client.File file = oList.RootFolder.Files.Add(fci);
                ctx.ExecuteQueryAsync(
                    (object s, ClientRequestSucceededEventArgs args) =>
                    {
                        this.Dispatcher.BeginInvoke(() =>
                        {
                            this.lblError.Content = "File was upload succesfully";
                            this.txtFileName.Text = "";
                            this.fName = "";
                            this.fs = null;
                            this.btnUpload.IsEnabled = true;
                            this.startUpload.IsEnabled = true;
                            this.startUpload.Content = "Start Upload";
                            HtmlPage.Window.Invoke("RefreshFilesList");
                        });
                    },
                    (object s, ClientRequestFailedEventArgs args) =>
                    {
                        this.Dispatcher.BeginInvoke(() =>
                        {
                            this.lblError.Content = "Request failed. " + args.Message + "\n" + args.StackTrace;
                        });
                    }
               );
            }
        }
    }
}

I am not going to explain all the code, but I will tell you what exactly is doing the trick. Giving a general example, the essence of the code can be reduced to this:

       // Create file stream and file name variables
        FileStream fs;
        String fName;

        // Create file dialog and use to assign value to the variable
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.Multiselect = false;

        if (dlg.ShowDialog() == true)
        {
            fs = dlg.File.OpenRead();
            fName = dlg.File.Name.ToString();
            this.txtFileName.Text = fName;
        }

        // Upload file
         using (ClientContext ctx = new ClientContext("https://sharepoint"))
         {
            Web web = ctx.Site.RootWeb;
            List oList = web.Lists.GetByTitle("Attachments");
            FileCreationInformation fci = new FileCreationInformation();
            // Here is the magic. Set ContentStream property of FileCreationInformation before execute query is doing the trick
            fci.ContentStream = fs;
            fci.Overwrite = true;
            fci.Url = "https://sharepoint/Attachments/" + fName;
            Microsoft.SharePoint.Client.File file = oList.RootFolder.Files.Add(fci); 
            ctx.ExecuteQueryAsync(success, fail) ;
         }  

I hope this will help. Happy coding! 🙂

Advertisements

Upload file to SharePoint with Silverlight

This is post is intended to respond to all developers who need a custom upload control. Majority of the solutions on internet are server side related, but in some cases only client side is allow and SharePoint Designer is the only tool you have.  The solution is a small Silverlight application which is able to convert a file into a byte array and pass it as encoded string to web service.

For the beginning, we need to create a class to read file from computer and transform it to base64 encoded string. The code below should do the work for you.

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.ComponentModel.DataAnnotations;
using System.Windows.Documents;

public class FileUpload
{
        BinaryReader reader;
        long fileSize;

        /// <summary>
        /// Initialize FileUpload class
        /// </summary>
        /// <param name="fs">FileStream</param>
        /// <param name="size">Size of file</param>
        public FileUpload(FileStream fs, long size)
        {
            reader = new BinaryReader(fs);
            fileSize = size;
        }

        /// <summary>
        /// Read stream and convert it into base64 encoded string
        /// </summary>
        /// <returns>Base64 encoded string</returns>
        [ScriptableMember()]
        public string ReadFully()
        {
            byte[] output = new byte[fileSize];
            for (long i = 0; i < fileSize; i++)
            {
                output[i] = reader.ReadByte(); //read until done
            }
            return Convert.ToBase64String(output);
        }

}

As we need to use some JavaScript functions here, we can create a static C# class to invoke these. This would make usage of JavaScript easier.

using System;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;

public static class JavaScriptInvoker
{
        /// <summary>
        /// Calling a javascript simple function without parameters
        /// </summary>
        /// <param name="fName">Function name</param>
        public static void Call(string fName)
        {
            try
            {
                HtmlPage.Window.Invoke(fName);
            }
            catch (Exception ex)
            {
                JavaScriptInvoker.Alert(ex.Message.ToString());
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="fName"></param>
        /// <param name="fParams"></param>
        public static void Call(string fName, string[] fParams)
        {
            try
            {
                HtmlPage.Window.Invoke(fName, fParams);
            }
            catch (Exception ex)
            {
                JavaScriptInvoker.Alert(ex.Message.ToString());
            }
        }

         /// <summary>
         /// Display an alert message in window
         /// </summary>
         /// <param name="message">Text to be displayed in alert</param>

        public static void Alert(string message)
        {
            HtmlPage.Window.Alert(message);
        }
}

The interface is simple. It contains a textbox and two button. Textbox (name:FileName) is displaying file name, browse button (name:UploadFile) opens file dialog picker and upload button (name:btnUpload) starts upload operation.

Silverlight upload

Here is the source for this page (MainPage.xaml).

public partial class MainPage : UserControl
{

        private FileStream fs;
        private string fName = "";

        public MainPage()
        {
            InitializeComponent();
        }

        // Browse for file to upload
        private void UploadFile_Click(object sender, RoutedEventArgs e)
        {

            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Multiselect = false;

            if (dlg.ShowDialog() == true)
            {
                fs = dlg.File.OpenRead();
                fName = dlg.File.Name.ToString();
                this.FileName.Text = fName;
            }
            else
            {
                fs = null;
                fName = "";
                this.FileName.Text = fName;
            }
        }

        private void btnUpload_Click(object sender, RoutedEventArgs e)
        {
            if ((this.fs != null) && (this.fName != ""))
            {
                App myApp = App.Current as App;

                FileUpload uploader = new FileUpload(this.fs, fs.Length);
                JavaScriptInvoker.Call("AttachFile", new string[] { myApp.SPListItemId, myApp.SPListName, this.fName, uploader.ReadFully() });
            }
        }

 }

SPListeItemId and SPListName are initiation parameters and be passed from dataformwebpart. AttachFile is JavaScript function accept list id, list item id, attachment file name and file base64 encoded as parameters and consume lists.asmx web service, using AddAttachment method.

Embed Silverlight in a SharePoint page.

Silverlight offer the possibility to work with SharePoint and execute some lists operations using Client Object Model. You can create an application and embed it to a SharePoint page using SharePoint Designer editor.

Embed Silverlight in SharePoint

You can note MS.SP.url initiation parameter which should be set to the url of you SharePoint. Without this you will receive an error using C# Silverlight Client Object Model.