Download Large Files from SharePoint Online

In my previous post, I had explained how to upload large files up to 10 GB to SharePoint Online. This is as per the new revised upload limit. The upload operation, as we know, is actually incomplete without download. I mistakenly, assumed that the previous code for file download will work for the latest larger files as well. But, just like I had to rewrite the upload code to accommodate this new limit, file download also needed a complete makeover. I initially tried the following ways with no success.

Read More »

Upload Large Files to SharePoint Online

Microsoft has now increased the SharePoint per file upload size limit to 10 GB. Here’s a full list of SharePoint Online limitations. Now, this has thrown up a number of new challenges for this upload to be carried out through code.

  • Cannot keep such a large file in a single .NET object.
  • It’s very difficult to upload a file of this magnitude in a single HTTP request. If ever, the connection fails, the entire upload would have to be restarted! For a large file, this could easily turn into a nightmare for us.

Read More »

Get Aggregate Value of a SharePoint Field using CSOM [Totals function]

This post is about directly applying aggregations on a SharePoint List, without specifying any grouping condition. If you want to group SharePoint List data before applying aggregation then, refer to this post, SharePoint Group By.

I must admit that this turned out to be little tricky than what I had imagined. The situation was fairly simple. I had a SharePoint List. I had applied couple of Totals functions, Standard Deviation & Variance, on one of its Number field in two separate custom views. I just wanted to retrieve those values from client side. That’s it! My initial assessment was there must be some function/property that I can easily query from the client. As it seemed initially, there’s no direct way of doing that. Fine, just another day at the office for a SharePoint developer 😉

Read More »

SPList.Items.Add() v/s SPList.AddItem()

The result of both the methods are same. They both add a new SPListItem to a SharePoint List (SPList) and returns back the newly created item. The difference lies in the process of creating the new items. SPList.Items.Add() explicitly adds a new item to the SharePoint List whereas SPList.AddItem() adds it implicitly.

In order to add a new item, it is required to get the current Lists schema. It is done by calling GetItems. This call takes place internally, so we don’t have any control over it. However, this is where the said two functions differs as they both execute different queries to get their respective ListItems.

SPList.Items.Add()

In the first case,

var newItem = list.Items.Add();

ListItemCollection is initialized using the following query before creating a new item:

new SPQuery()
{
    ViewAttributes = "Scope=\"Recursive\""
});

SPList.AddItem() 

In this case,

var newItem = list.AddItem();

ListItemCollection is initialized using the following query:

new SPQuery()
{
    Query = "-1",
    ViewFields = ""
};

We can clearly see that this is a fake query as it is querying for a ListItem whose ID is -1! It means that the ListItemCollection will always be zero while adding a new item.


This difference gets reflected in the execution time for these queries. Expectedly, the second query, SPList.AddItem(), is much more faster especially while dealing with a List with large number of items.

Delete a ListItem from SharePoint ListView WebPart [XSLT]

The objective here is to delete a single or multiple ListItems from a single SharePoint List using a ListView WebPart i.e., to delete a ListItem from client side. In order to achieve this, we will be making an ajax call to the SharePoint WebService, Lists.asmx. SharePoint provides a number of WebServices with various functionalities. One of them is Lists.asmx. This service has many apis related with SharePoint List. We’re gonna call UpdateListItems api with the Delete command to delete a single or multiple ListItems accordingly. The url of this service is kept relative as, ../_vti_bin/lists.asmx.Following is the schema of this api.

POST /_vti_bin/Lists.asmx HTTP/1.1
Host: win-hk4iec2ge2r
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://schemas.microsoft.com/sharepoint/soap/UpdateListItems"

<?xml version="1.0" encoding="utf-8"?>
 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 <soap:Body>
 <UpdateListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">
 <listName>string</listName>
 <updates>string</updates>
 </UpdateListItems>
 </soap:Body>
</soap:Envelope>

The approach is going to be pretty simple. We’ll be editing our custom XSLT as a normal HTML file with an exception of encoding the HTML schema of the api as we can not use any html tag inside an xml declaration. First we’ll define our HTML structure in the XSLT. Here, we will generate an HTML checkBox against the Title of all the items displayed. The IDs of these checkBoxes are the SharePoint ID of the corresponding ListItem they represent. This will help us to get the IDs of the ListItem that User wants to delete.



<table style="width:100%; padding-left: 0px">

<tr>

<td colspan="2" style="text-align: left; width: 40%"> 
 <input id="chkSelectAll" type="checkbox" name="checkbox" onclick="OnChangeCheckbox(this)" value="SelectAll" />
 <label id="SelectAll">Select All</label>
 </td>


<td style="text-align: left;">
 <button type="button" onclick="DeleteItems()" class="buttonv">Delete</button>
 </td>

 </tr>

</table>

Next declare the checkbox where the code for, for-each rows are written. As mentioned, the checkBoxes will appear against the title for each item in the view.


<div>


 <input type="checkbox" name="checkbox" value="{@ID}" id="{@ID}" onclick="ClearParent(this)"/>
 <a class="list_link" title="{@ListId_x003a_Title}" href="viewList.aspx?ListId={@ListId_x003a_ID}">
 <xsl:value-of select="@ListId_x003a_Title" disable-output-escaping="yes" />
 </a>
 
 <span class="submittedby"></span>
 
 <span class="summary">
 <xsl:value-of select="@ListId_x003a_ShortSummary" disable-output-escaping="yes" />
 </span>
 

</div>

Now all the JavaScript required to complete this operation.

        
        //This function gets the ID of all the ListItems that the user has selected and mark them for Deletion.
        function GetItemsToBeRemoved(){
          var counter = 0;
          var checkedIds = new Array();
          var cbs = document.getElementsByTagName('input');
          for(var i=0; i < cbs.length; i++) {               if(cbs[i].type == 'checkbox') {                   if(cbs[i].value != "SelectAll"){                       if(cbs[i].checked){                         checkedIds[counter++] = cbs[i].id;                       }                   }               }           }           return checkedIds;         }         //This is the function which is called on the Delete Button Click event. Only "" without encoding.         function DeleteItems(){           var checkedIds = GetItemsToBeRemoved();           if(checkedIds.length > 0){
            GenerateRequest(checkedIds);
          }
          else{
            alert('Plz select atleast 1 item to delete.');
          }
        }
        
        //Create and send the SOAP request. Again HTML tags are encoded. Since we'll be generating a Batch request for deletion, we have to generate this request in a for-loop.
        function GenerateRequest(checkedIds){

            var soapEnv = "<?xml version='1.0' encoding='utf-8'?><soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'><soap:Body><UpdateListItems xmlns='http://schemas.microsoft.com/sharepoint/soap/'><listName>SpListName</listName><updates><Batch>";
            var methodEnv1 = "<Method ID='1' Cmd='Delete'><Field Name='ID'>";
            var methodEnv2 = "</Field></Method>";
            var soapEnv2 = "</Batch></updates></UpdateListItems></soap:Body></soap:Envelope>";

 
            //Foreach selected item, create a delete request. 
            for (var i = 0; i < checkedIds.length; i++){
                soapEnv+=methodEnv1 + checkedIds[i] + methodEnv2;
            };

            soapEnv+=soapEnv2;

            //Finally send a single batch request for multiple items. 
            $.ajax({
                    url: "../_vti_bin/lists.asmx",
                    beforeSend: function(xhr) { xhr.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");
                },
                type: "POST",
                dataType: "xml",
                data: soapEnv,
                complete: CheckForErrors,
                contentType: "text/xml; charset=\"utf-8\""
            });
        }

        //This function checks if any error has been encountered or not. If no error is found, then it re-Loads the current page to refresh the View.
        function CheckForErrors(xData, status) {

            //alert("Update Status : " + status );
            //alert("Update Status : " + xData.responseText );

            if(status == "success"){
                location.reload(true);
            }

        }

        //helper functions
        
        //Disable the delete button if there are no items to be displayed i.e., the SharePoint List is empty. Call this function on document.ready.
        function ValidateNoRecords(){
            var isDataFound = false;
            var cbs = document.getElementsByTagName('input');
            for(var i=0; i < cbs.length; i++) {
                if(cbs[i].type == 'checkbox') {
                    if(cbs[i].value != "SelectAll"){
                        isDataFound = true;
                        break;
                    }
                }
            }

            if(!isDataFound){
                var id = "#chkSelectAll";
                $(id).closest('tr').hide()
            }

        }

        //Checks whether to display the Delete button or not.
        $(document).ready(function(){
            ValidateNoRecords();
        })

***