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

Programatically import SharePoint list into Microsoft Access

As strange as it seems for some IT professionals, days when VBA was often used to Office application are not dead. In some environments, VBA programming remains the easiest way Microsoft Office users and developers can create solutions. And, as one of the strongest integration is between SharePoint 2010 and Microsoft Access, I have decided to approach one the main, which is how to import SharePoint into the local database from code.

Without more explanations, I thing code below is easy.

On Error GoTo error

' Variables definition here
Dim tbl As DAO.TableDef
Dim fld As Field


'Delete table if exists
For Each tbl In CurrentDb.TableDefs
    If tbl.Name = "Table name" Then
        DoCmd.DeleteObject acTable, "Table name"
        Exit For
    End If
Next


'Import SharePoint list
DoCmd.TransferSharePointList acImportSharePointList, "http://urlofthesite", "{A92FF376-FC2E-4390-BB03-CC47C756EB08}", "{F1AA137A-7C97-4C89-AB79-31447B77545C}", "Table name", True

'Create column a column to work with
CurrentDb.TableDefs("Table name").Fields.Append CurrentDb.TableDefs("Table name").CreateField("My local text field", dbText, 255)


exitOnError:
    Exit Sub

error:
    MsgBox Err.Description
    'In case of error, delete imported table to empty the memory
    DoCmd.DeleteObject acTable, "Table name"
    Resume exitOnError

End Sub

The code is deleting the table if already exists in the local database from the previous operation, import the list based on specified view and list id and create a local field to work with. Typically, developers create a local table, often containing more columns to work with than SharePoint list, import the list, append the list data into local table and update the data into local table, for the main reason they can have there as many field as they want without affecting the SharePoint list. But no need for this. Everything can be done in less operations. Once the SharePoint list is imported, developer can perform as many operations he wants without affecting SharePoint data, which in most of the cases is business critical.

The key is DoCmd.TransferSharePointList, which is very well documented here.