SharePoint multi-file upload control with metadata using uploadify and HttpHandler


Requirement:

To upload bulk documents inside sharepoint with metadata values.

Multiupload selector should be something with Gmail selector.

The control would look something like this.

uploadcontrol

Design Limitations:

ASP fileupload control works great for single file uploads. But for multi-file uploads one known way is to design multi upload control in SharePoint is to modify “/_layouts/UploadEx.aspx”. Since its not recommended to touch SharePoint native files under _layouts folder and modifying the copied version to add metadata fields seems to be complicated I decided to write a Visual webpart.

Design:

Though many JQuery libraries and ajax controls are available to design multi-file upload controls, uploadify.js (http://www.uploadify.com/) proved to be a perfect fit for my requirement since it had features to supply form metadata values on upload.

How uploadify works:

Uploadify basically transforms an input file control to support event handling at individual file levels. Then each file objects are passed to the HTTP handler functions to do uploads on file system. In our case its SharePoint Document library.

Simple uploadify function looks like this.

 $(document).ready(function () {

$("#uploadControl").uploadify({
 'swf': '/_layouts/MultiUploadWP/uploadify.swf',
 'cancelImg': '/_layouts/MultiUploadWP/uploadify-cancel.png',
 'buttonText': 'Upload Files',
 'uploader': '/_layouts/MultiUploadWP/Upload.ashx',
 'method': 'post',
 'multi': true,
'preventCaching' : false

 });
 });

<input id="uploadControl"  type="file" />

Where “SWF”,”cancelImg”.png are the extracted from uploadify download and placed inside _layouts folder. Other options for uplaodify is documented at http://www.uploadify.com/documentation/.

Make sure flash player is installed and browser add-ons are enabled.

“uploader” parameter is the heart of uploadify where each selected file will be passed to a http handler function (Upload.ashx) to achieve the required file handling.

Now create a Visual webpart and add uploadify function in the document ready function


<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UploadWPUserControl.ascx.cs" Inherits="<<assembly info>>" %>
<script src="/_layouts/MultiUploadWP/jquery.js" type="text/javascript"></script>
<script src="/_layouts/MultiUploadWP/jquery.uploadify.min.js" type="text/javascript"></script>
<link href="/_layouts/MultiUploadWP/uploadify.css" rel="stylesheet" type="text/css" />

<h3>
 <font color="#A69E9D">Upload your reports here</font></h3>
<br />
<div>
 <h4>
 <font color="#786E6D">Select Document Type</font></h4>
 <asp:DropDownList ID="docTypeDDL" runat="server">
 </asp:DropDownList>
</div>
<br />
<input id="uploadControl" name="uploadControl" type="file" />
<p>
 <asp:Label ID="lblError" runat="server" BackColor="Red" ></asp:Label>
</p>

<script type="text/javascript">
 $(document).ready(function () {

$("#uploadControl").uploadify({
 'swf': '/_layouts/Advent_CarryHolder/uploadify.swf',
 'cancelImg': '/_layouts/Advent_CarryHolder/uploadify-cancel.png',
 'buttonText': 'Upload Files',
 'uploader': '/_layouts/Advent_CarryHolder/Upload.ashx',
 'method': 'post',
 'multi': true,
 'preventCaching' : false,
 'onUploadStart': function (file) {

var ddlSelect = $('#<%= docTypeDDL.ClientID%>');
 $('#uploadControl').uploadify('settings', 'formData', { 'docType': ddlSelect[0].value });
 }

 });
 });
</script>

“onUploadStart” function will be triggered at the start of each file upload. Here we can pass metadata values to ashx handler using  uploadify(‘settings’, ‘formData’, {‘key’,’value’ }); functions.

DropDown control ddlSelect is binded to look up list and selected value is passed as key value pair { ‘docType’: ddlSelect[0].value } to the ashx.

How ASHX works:

ashx file implementation for uploadify is explained at http://www.uploadify.com/forum/#/discussion/45/asp-net-c-code-to-replace-upload-php-upload-ashx/p1.  where the class is derived form IHTTPHandler and implements ProcessRequest(HttpContext context).

Now create upload.ashx file using notepad ( Visual studio – SharePoint extensions doesnot have template for this) and drop it at _layouts folder

<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ WebHandler Language="C#" Class="Upload" %>
using System;
using System.Web;
using Microsoft.SharePoint;
using System.IO;
using System.Collections;

public class Upload : IHttpHandler
{
private Guid siteId = SPContext.Current.Site.ID;
private Guid webId = SPContext.Current.Web.ID;
private string uploadFileName = null;
private string[] fileNameinArray = null;

public void ProcessRequest(HttpContext context)
{
try
{
string docType = context.Request.Form["docType"];
HttpPostedFile uploadedfile = context.Request.Files["Filedata"];

if (uploadedFile.ContentLength > 0)

{
uploadFileName = Path.GetFileName(uploadedFile.FileName);
byte[] file_content = new byte[Convert.ToInt32(uploadedFile.ContentLength)];
uploadedFile.InputStream.Read(file_content, 0, Convert.ToInt32(uploadedFile.InputStream.Length));
fileNameinArray = uploadFileName.Split('_');

using (SPSite site = new SPSite(siteId))
{
using (SPWeb web = site.OpenWeb(webId))
{

SPList contentList = web.Lists["Contents"];
SPList partnershipList = web.Lists["Partnerships"];
SPListItem partnershipItem = null;

foreach (SPListItem lstPartnershipItem in partnershipList.Items)
{

if (lstPartnershipItem["Codename"].ToString() == fileNameinArray[2])
{
partnershipItem = lstPartnershipItem;
break;
}
}

if (partnershipItem != null)
{
web.AllowUnsafeUpdates = true;
SPFolderCollection contentFolders = contentList.RootFolder.SubFolders;
foreach (SPFolder namedFolder in contentFolders)
{

if (namedFolder.Name.Equals(fileNameinArray[1]))
{

SPFile newContentListItem = namedFolder.Files.Add(uploadFileName, file_content, true);
SPListItem item = newContentListItem.Item;
item.File.CheckOut();
item["Partnership"] = new SPFieldLookupValue(partnershipItem.ID, partnershipItem.Title);
item.Update();
item.File.CheckIn("Initial submission");

}
}

}
catch (Exception ex)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Error: " + ex.Message);
}
}

#endregion
}

Input file can be accessed by context.Request.Files[“FileData”] and parameters can be accessed by context.Request.Form[“docType”].

Now package the WSP and deploy the solution.

Troubleshooting ASHX files:

Since ASHX files have inline code its not managed by Visual studio. Hence it doesn’t show any compiler errors. Some times clicking uploadify button might spit http 500 errors which means ashx might have compiler errors. To resolve navigate to the file location via browser “https:\\site\_layouts\MultiUploadWP\Upload.ashx” to get the exception details. If everything is resolved it can be debugged like debugging any other webparts by attaching to w3wp process.

Advertisements