There were many tutorial about uploading files to REST service but there wasn't much on how to create service that upload file inside SharePoint 2013. Base on many reference on internet I've created a REST service which run under SharePoint 2013 project (Full trust code). So how do you add a svc file inside SharePoint 2013 project? You can search using this keyword "creating a custom svc in SharePoint 2013" on google. Or just create that svc on another project and put it under ISAPI directory on SharePoint project.
Then we start with setting up interface and define class definition that will become the output from our REST service.
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; using System.Threading.Tasks; namespace UploadFileUsingREST { [ServiceContract] interface IUploadRest { [OperationContract] [WebInvoke(Method = "POST", UriTemplate = "UploadFile/{fileName}/{title}")] ConfirmationFileUpload Upload(string fileName, string title, Stream fileContent); } [DataContract] public class ConfirmationFileUpload { [DataMember] public long FileReceivedLength; [DataMember] public bool IsSuccess; [DataMember] public string ErrorMessage; } }
After that we begin implementing code that will receive stream and update property of that file.
using Microsoft.SharePoint; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.ServiceModel.Activation; using System.Text; using System.Threading.Tasks; namespace UploadFileUsingREST { [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class UploadRest : IUploadRest { public const string SiteURL = http://siteurl; public ConfirmationFileUpload Upload(string fileName, string title, System.IO.Stream fileContent) { ConfirmationFileUpload result = new ConfirmationFileUpload(); try { using (SPSite site = new SPSite(SiteURL)) { using (SPWeb web = site.OpenWeb()) { using (MemoryStream writer = new MemoryStream()) { int readCount; byte[] buffer = new byte[8192]; while ((readCount = fileContent.Read(buffer, 0, buffer.Length)) != 0) writer.Write(buffer, 0, readCount); web.AllowUnsafeUpdates = true; SPFile file = web.Files.Add(string.Format("{0}/Shared Documents/{1}", SiteURL, fileName), writer, true); file.Update(); SPListItem item = file.ListItemAllFields; item[SPBuiltInFieldId.Title] = title; item.Update(); web.AllowUnsafeUpdates = false; result.FileReceivedLength = file.Length; } } } result.IsSuccess = true; } catch (Exception e) { //log to ULS result.IsSuccess = false; result.ErrorMessage = e.Message; } finally { fileContent.Close(); } return result; } } }
The code was simple that it will receive the stream and save it to MemoryStream first before add it using Files.Add. Then we get listitem and update its property. You need to setting AllowUnsafeUpdates to true before add files to prevent error page need revalidation.
At this point you can try upload files using this REST service, but SharePoint limit max receive content length to only 18KB. Googling it out many people suggesting to define service reference in web.config, but it won't work. You can't change svc endpoint on web.config or on anything else as I aware of, it took me two days to figure this out before I found this site. It needs to be done on code (or PowerShell?) . We need to set that maxReceiveMessageSize larger than it's default value 18KB. How do you add those FeaturedInstalled method in SharePoint 2013? I googled and found another site that explained this. Basically you need to override a FeaturedInstalled method and set that endpoint in there. Wrapping it all together, this is what I did. We need to set maxreceivecontent on wcfServiceSetting for our REST service file. We can set it during feature deployment.
- Add a new class, I name it FeatureEvent.cs
- Inherits that class to SPFeatureReceiver
- Override method FeatureInstalled
- Then add code to change ReaderQuotasMaxStringContentLength, ReaderQuotasMaxArrayLength, ReaderQuotasMaxBytesPerRead, MaxReceivedMessageSize just like bellow
using Microsoft.SharePoint; using Microsoft.SharePoint.Administration; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UploadFileUsingREST { public class FeatureEvent : SPFeatureReceiver { public override void FeatureInstalled(SPFeatureReceiverProperties properties) { SPWebService contentService = SPWebService.ContentService; SPWcfServiceSettings wcfServiceSettings = new SPWcfServiceSettings(); wcfServiceSettings.ReaderQuotasMaxStringContentLength = Int32.MaxValue; wcfServiceSettings.ReaderQuotasMaxArrayLength = Int32.MaxValue; wcfServiceSettings.ReaderQuotasMaxBytesPerRead = Int32.MaxValue; wcfServiceSettings.MaxReceivedMessageSize = Int32.MaxValue; contentService.WcfServiceSettings["uploadrest.svc"] = wcfServiceSettings; contentService.Update(true); } } }
We then need to create a feature that will run that file when the feature got installed.
- Create a new SharePoint feature file
- Open that feature and click on Manifest tab
- Expand Edit option
- Then add ReceiverAssembly and ReceiverClass just like bellow
<?xml version="1.0" encoding="utf-8" ?> <Feature xmlns="http://schemas.microsoft.com/sharepoint/" ReceiverAssembly="UploadFileUsingREST, Version=1.0.0.0, Culture=neutral, PublicKeyToken=282a31b1b9f5b608" ReceiverClass="UploadFileUsingREST.FeatureEvent"> </Feature>
Deploy it and test it, I've created a console program to test this svc
class Program { static void Main(string[] args) { FileInfo fi = new FileInfo(@"C:\testfile.pdf"); Console.WriteLine(fi.Length); string urlFull = string.Format("http://documentrepositorysite/_vti_bin/uploadrest.svc/UploadFile/{0}/{1}", fi.Name, fi.Name); HttpWebRequest client = (HttpWebRequest)WebRequest.Create(urlFull); client.Credentials = new NetworkCredential("username", "userpassword", "userdomain"); client.Method = "POST"; client.UseDefaultCredentials = true; client.PreAuthenticate = true; using (var requestStream = client.GetRequestStream()) { using (var file = fi.OpenRead()) { file.CopyTo(requestStream); } } using (var response = client.GetResponse()) { StreamReader reader = new StreamReader(response.GetResponseStream()); string respon = reader.ReadToEnd(); Console.WriteLine(respon); Console.ReadLine(); } } }
Testing it with 11MB file, and it completed in 10s.
Complete source code
No comments:
Post a Comment