rest api方式实现对文档库的管理[转]
写在前面
刚入职一家新公司,在对接app的时候需要获取到某公司的sharepoint上面的文档库,获取文档库列表,团队文档库中的文件和文件夹列表,个人文档库中的文件文件夹列表,及在app端进入文件夹的时候需要获取该文件夹下的文件及文件夹列表,对文件的上传下载等操作。
对rest api的使用,完全是小白,具体也不知道怎么实现,在编写过程中查找了很多资料,发现这方面的资料极其少,也有可能是对自己对这个技术完全的不了解,所以在查找方向上面有问题。最后算是实现了上面的功能,这里做一下记录,以及提供一些参考资料。
实现过程
在如果注册app以及注册发布者的操作步骤,可以参考我之前的一篇文章:Rest API的简单应用,这里介绍了如何注册app和发布者的操作步骤,也包括了如何使用高安全的访问方式。希望对你有所帮助。项目的简单描述,本项目实现方式是一个server to server的方式,在app请求iis上面部署的接口,然后该iis服务器再去请求sharepoint服务器的api,获取文档库的相关信息。流程是大概这样的一个流程。
注册的发布者:所有的操作可以使用同一个发布者。
团队文档库:可以使用同一个ClientID
个人文档库:每一个人对应一个ClientID。
查询方式使用了OData的方式,关于OData的查询方式,可以参考这篇文章
https://msdn.microsoft.com/zh-SG/library/fp142385
查询
查询单个文件的信息
/// <summary> /// 根据文件在服务端的相对url获取文件信息 /// </summary> /// <param name="serverRelativeUrl">文件在服务端的相对url</param> /// <returns>文件信息的json</returns> public string GetFileInfoByRelativeUrl(string serverRelativeUrl) { ///Personal/chenwd/Documents/Shared with Everyone string queryFileApi = "GetFileByServerRelativeUrl('" + serverRelativeUrl + "')"; string filter = "?$select=" + new Document.Core.Model.Document().ToString(); if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } string strFileJson = RequestHelper.RequestGet(_userName, _appUrl, queryFileApi + filter, _documentType); return strFileJson.ToJson("200"); }
这里将文件信息封装成为一个类,并重写了tostring方法,方便操作。
/// <summary> /// 文档信息实体类 /// </summary> public class Document { /// <summary> /// 签入描述 /// </summary> public string CheckInComment { get; set; } /// <summary> /// 签出类型 /// </summary> public string CheckOutType { get; set; } /// <summary> /// 内容Tag /// </summary> public string ContentTag { get; set; } /// <summary> /// CustomizedPageStatus /// </summary> public string CustomizedPageStatus { get; set; } /// <summary> /// 独占签出标识 /// </summary> public string ETag { get; set; } /// <summary> /// 是否存在 /// </summary> public string Exists { get; set; } /// <summary> /// 大小 /// </summary> public string Length { get; set; } /// <summary> /// 等级 /// </summary> public string Level { get; set; } /// <summary> /// 主要版本 /// </summary> public string MajorVersion { get; set; } /// <summary> /// 次版本 /// </summary> public string MinorVersion { get; set; } /// <summary> /// 文档名称 /// </summary> public string Name { get; set; } /// <summary> /// 文档服务端相对地址 /// </summary> public string ServerRelativeUrl { get; set; } /// <summary> /// 创建时间 /// </summary> public string TimeCreated { get; set; } /// <summary> /// 最后一次修改时间 /// </summary> public string TimeLastModified { get; set; } /// <summary> /// 描述 /// </summary> public string Title { get; set; } /// <summary> /// 版本 /// </summary> public string UIVersion { get; set; } /// <summary> /// 版本标识 /// </summary> public string UIVersionLabel { get; set; } public override string ToString() { Type type = this.GetType(); PropertyInfo[] pros = type.GetProperties(); string str = string.Empty; foreach (var item in pros) { str += item.Name + ","; } return str.TrimEnd(','); } }
然后使用Odata查询,获取需要的字段的值。
请求辅助类
/// <summary> /// 请求辅助类 /// </summary> public class RequestHelper { public static string ClientID; /// <summary> /// get请求 /// </summary> /// <returns></returns> public static string RequestGet(string userName, string appUrl, string apiUrl, DocumentType docType) { string strJson = string.Empty; Uri hostWebUri = new Uri(appUrl); try { CheckDcomentType(docType); var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(hostWebUri, null); HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(appUrl + "/_api/web/" + apiUrl); endpointRequest.Method = RequestMethodType.GET.ToString(); endpointRequest.Accept = "application/json;odata=verbose"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); StreamReader sr = new StreamReader(endpointResponse.GetResponseStream(), Encoding.UTF8); strJson = sr.ReadToEnd(); sr.Dispose(); } catch (Exception ex) { strJson = string.Empty.ToErrorJson("500", ex.Message); } return strJson; } /// <summary> /// 下载文件get请求 /// </summary> /// <param name="appUrl"></param> /// <param name="apiUrl"></param> /// <param name="filePath"></param> /// <param name="fileLength"></param> public static void RequestGet(string appUrl, string apiUrl, string filePath, int fileLength, DocumentType docType) { byte[] buffer = null; Uri hostWebUri = new Uri(appUrl); CheckDcomentType(docType); var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(hostWebUri, null); HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(appUrl + "/_api/web/" + apiUrl); endpointRequest.Method = "Get"; endpointRequest.Accept = "application/json;odata=verbose"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { using (Stream st = endpointResponse.GetResponseStream()) { BinaryReader br = new BinaryReader(st); buffer = new byte[fileLength]; br.Read(buffer, 0, buffer.Length); fs.Write(buffer, 0, buffer.Length); } } } /// <summary> /// 检测是否是个人文档库的请求 /// </summary> /// <param name="docType">文档类型</param> private static void CheckDcomentType(DocumentType docType) { if (docType == DocumentType.Personal) { if (string.IsNullOrEmpty(ClientID)) { throw new ArgumentNullException("ClientID不能为空"); } else { TokenHelper.ClientId = ClientID.ToLower(); } } } /// <summary> /// post请求 /// </summary> /// <returns></returns> public static string RequestPost(string userName, string appUrl, string apiUrl, DocumentType docType) { string strJson = string.Empty; Uri hostWebUri = new Uri(appUrl); try { CheckDcomentType(docType); var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(hostWebUri, null); HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(appUrl + "/_api/web/" + apiUrl); endpointRequest.Method = RequestMethodType.POST.ToString(); endpointRequest.Accept = "application/json;odata=verbose"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); //post请求处理 endpointRequest.ContentLength = 0; endpointRequest.ContentType = "application/json;odata=verbose"; HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); StreamReader sr = new StreamReader(endpointResponse.GetResponseStream(), Encoding.UTF8); strJson = sr.ReadToEnd().ToSuccessJson("200","操作成功"); sr.Dispose(); } catch (WebException ex) { strJson = string.Empty.ToErrorJson("500", ex.Message); } return strJson; } /// <summary> /// put请求 /// </summary> /// <returns></returns> public static string RequestPut(string userName, string appUrl, string apiUrl, DocumentType docType) { string strJson = string.Empty; Uri hostWebUri = new Uri(appUrl); try { CheckDcomentType(docType); var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(hostWebUri, null); HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(appUrl + "/_api/web/" + apiUrl); endpointRequest.Method = RequestMethodType.PUT.ToString(); endpointRequest.Accept = "application/json;odata=verbose"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); StreamReader sr = new StreamReader(endpointResponse.GetResponseStream(), Encoding.UTF8); strJson = sr.ReadToEnd(); sr.Dispose(); } catch (Exception ex) { strJson = string.Empty.ToErrorJson("500", ex.Message); } return strJson; } /// <summary> /// delete请求 /// </summary> /// <returns></returns> public static string RequestDelete(string userName, string appUrl, string apiUrl, DocumentType docType) { string strJson = string.Empty; Uri hostWebUri = new Uri(appUrl); try { CheckDcomentType(docType); var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(hostWebUri, null); HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(appUrl + "/_api/web/" + apiUrl); endpointRequest.Method = RequestMethodType.DELETE.ToString(); endpointRequest.Accept = "application/json;odata=verbose"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); StreamReader sr = new StreamReader(endpointResponse.GetResponseStream(), Encoding.UTF8); strJson = sr.ReadToEnd(); sr.Dispose(); } catch (Exception ex) { strJson = string.Empty.ToErrorJson("500", ex.Message); } return strJson; } /// <summary> /// Merge请求 /// </summary> /// <returns></returns> public static string RequestMerge(string userName, string appUrl, string apiUrl, DocumentType docType) { string strJson = string.Empty; Uri hostWebUri = new Uri(appUrl); try { CheckDcomentType(docType); var accessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(hostWebUri, null); HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(appUrl + "/_api/web/" + apiUrl); endpointRequest.Method = RequestMethodType.MERGE.ToString(); endpointRequest.Accept = "application/json;odata=verbose"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); StreamReader sr = new StreamReader(endpointResponse.GetResponseStream(), Encoding.UTF8); strJson = sr.ReadToEnd(); sr.Dispose(); } catch (Exception ex) { strJson = string.Empty.ToErrorJson("500", ex.Message); } return strJson; } }
为string做扩展方法,将结果使用自定义的格式包裹成json格式。
/// <summary> /// string类型扩展 /// </summary> public static class StringExtention { /// <summary> /// 将json数据包裹在code/time/data中 /// </summary> /// <param name="source">json源</param> /// <param name="strCode">状态码</param> /// <returns>json数据</returns> public static string ToJson(this string source, string strCode) { StringBuilder sb = new StringBuilder(); sb.Append("{"); sb.Append("\"_code\":"); sb.Append("\"" + strCode + "\","); sb.Append("\"_time\":"); sb.Append("\"" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\","); sb.Append("\"_data\":"); sb.Append("{"); sb.Append("\"files\":"); sb.Append(source); sb.Append("}}"); return sb.ToString(); } /// <summary> ///拼接json字符串 /// </summary> /// <param name="source">FolderJson字符串</param> /// <param name="strCode">是否成功编码</param> /// <param name="strFileInput">要拼接的Filejson字符串</param> /// <returns>拼接后的json字符串</returns> public static string ToMergeJson(this string source, string strCode, string strFileInput) { StringBuilder sb = new StringBuilder(); sb.Append("{"); sb.Append("\"_code\":"); sb.Append("\"" + strCode + "\","); sb.Append("\"_time\":"); sb.Append("\"" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\","); sb.Append("\"_data\":"); sb.Append("{"); sb.Append("\"folders\":"); if (!string.IsNullOrEmpty(source)) { sb.Append(source); } else { sb.Append("{"); sb.Append("\"d\":"); sb.Append("{"); sb.Append("\"results\":"); sb.Append("["); sb.Append("{}]"); sb.Append("}}"); } sb.Append(","); sb.Append("\"files\":"); sb.Append(strFileInput); sb.Append("}}"); return sb.ToString(); } /// <summary> /// 获得下载的文件路径 /// </summary> /// <param name="source">源json</param> /// <param name="strCode">响应码</param> /// <param name="strUrl">下载的url</param> /// <returns>下载的url</returns> public static string ToDownLoadJson(this string source, string strCode, string strUrl) { StringBuilder sb = new StringBuilder(); sb.Append("{"); sb.Append("\"_code\":"); sb.Append("\"" + strCode + "\","); sb.Append("\"_time\":"); sb.Append("\"" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\","); sb.Append("\"_data\":"); sb.Append("{"); sb.Append("\"Url\":"); sb.Append("\"" + strUrl + "\""); sb.Append("}}"); return sb.ToString(); } /// <summary> /// 返回操作成功的json /// </summary> /// <param name="source">源字符串</param> /// <param name="strCode">操作码</param> /// <param name="message">操作提示信息</param> /// <returns>json</returns> public static string ToSuccessJson(this string source, string strCode, string message) { StringBuilder sb = new StringBuilder(); sb.Append("{"); sb.Append("\"_code\":"); sb.Append("\"" + strCode + "\","); sb.Append("\"_time\":"); sb.Append("\"" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\","); sb.Append("\"_data\":"); sb.Append("{"); sb.Append("\"Message\":"); sb.Append("\"" + message + "\""); sb.Append("}}"); return sb.ToString(); } /// <summary> /// 错误信息提示 /// </summary> /// <param name="source">源json</param> /// <param name="strCode">错误码</param> /// <param name="message">错误信息</param> /// <returns>错误信息</returns> public static string ToErrorJson(this string source, string strCode, string message) { StringBuilder sb = new StringBuilder(); sb.Append("{"); sb.Append("\"_code\":"); sb.Append("\"" + strCode + "\","); sb.Append("\"_time\":"); sb.Append("\"" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\","); sb.Append("\"_data\":"); sb.Append("{"); sb.Append("\"Message\":"); sb.Append("\"" + message + "\""); sb.Append("}}"); return sb.ToString(); } }
查询某个相对服务端的Url下的所有文件及文件夹
/// <summary> /// 根据文档在服务端的相对Url获取文档信息 /// </summary> /// <param name="serverRelativeUrl"></param> /// <returns></returns> public string GetDocmumentsByRelativeUrl(string serverRelativeUrl) { ///Personal/chenwd/Documents/Shared with Everyone string queryFileApi = "GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Files"; string queryFolderApi = "GetFolderByServerRelativeUrl('" + serverRelativeUrl + "')/Folders"; string filter = "?$select=" + new Document.Core.Model.Document().ToString(); if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } string strFileJson = RequestHelper.RequestGet(_userName, _appUrl, queryFileApi + filter, _documentType); string strFolderJson = RequestHelper.RequestGet(_userName, _appUrl, queryFolderApi + filter, _documentType); return strFolderJson.ToMergeJson("200", strFileJson); }
根据文档库的URL查询所有的文件及文件夹
/// <summary> /// 根据url获取该url下的所有的文档信息 /// </summary> /// <param name="docSiteUrl"></param> /// <returns></returns> public string GetDocumentsByUrl(string docSiteUrl) { //docsiteUrl格式:http://my.xxx.com/Personal/cxxx/Documents/Forms/All.aspx //解析出文档库服务端的相对路径 int startIndex = docSiteUrl.IndexOf("//") + 2; string strRelativeUrl = docSiteUrl.Substring(startIndex); string[] strUrls = strRelativeUrl.Split(new char[] { '/' }); strRelativeUrl = "/" + strUrls[1] + "/" + strUrls[2] + "/" + strUrls[3] + "/"; string queryFileApi = "GetFolderByServerRelativeUrl('" + strRelativeUrl + "')/Files"; string queryFolderApi = "GetFolderByServerRelativeUrl('" + strRelativeUrl + "')/Folders"; string filter = "?$select=" + new Document.Core.Model.Document().ToString(); if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } string strFileJson = RequestHelper.RequestGet(_userName, _appUrl, queryFileApi + filter, _documentType); string strFolderJson = RequestHelper.RequestGet(_userName, _appUrl, queryFolderApi + filter, _documentType); return strFolderJson.ToMergeJson("200", strFileJson); }
移动文件
/// <summary> /// 移动 /// </summary> /// <param name="serverFileUrl"></param> /// <param name="newFileUrl"></param> /// <returns></returns> public string MoveTo(string serverFileUrl, string newFileUrl) { string strMoveToApi = "GetFileByServerRelativeUrl('" + serverFileUrl + "')/moveto(newurl='" + newFileUrl + "',flags=1)"; if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } return RequestHelper.RequestPost(_userName, _appUrl, strMoveToApi, _documentType); }
/// <summary> /// 创建文件夹 /// </summary> /// <param name="serverRelativeUrl"></param> /// <param name="folderName"></param> /// <returns></returns> public string CreateFolder(string serverRelativeUrl, string folderName) { serverRelativeUrl = serverRelativeUrl.StartsWith("/", StringComparison.CurrentCulture) ? serverRelativeUrl : "/" + serverRelativeUrl; string createFolderApi = "folders/add('" + serverRelativeUrl + "/" + folderName + "')"; if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } return RequestHelper.RequestPost(_userName, _appUrl, createFolderApi, _documentType); }
将所需的字段封装为文档库实体,方便以后的操作
/// <summary> /// 文档库实体类 /// </summary> public class DocumentSite { /// <summary> /// 文档库id /// </summary> public string Id { set; get; } /// <summary> /// 文档库父Url /// </summary> public string ParentWebUrl { set; get; } /// <summary> /// 文档库的英文标识名字 /// </summary> public string EntityTypeName { set; get; } /// <summary> /// 当前文档库文档数 /// </summary> public string ItemCount { set; get; } /// <summary> /// 最后一次删除时间 /// </summary> public string LastItemDeletedDate { set; get; } /// <summary> /// 最后一次修改时间 /// </summary> public string LastItemModifiedDate { set; get; } /// <summary> /// 文档库名称 /// </summary> public string Title { set; get; } /// <summary> /// 创建时间 /// </summary> public string Created { set; get; } /// <summary> /// 返回所有的属性,以逗号分隔 /// </summary> /// <returns></returns> public override string ToString() { Type type = this.GetType(); PropertyInfo[] pros = type.GetProperties(); string str = string.Empty; foreach (var item in pros) { str += item.Name + ","; } return str.TrimEnd(','); } } DocumentSite
根据文档库的模板获取所有的文档库列表json
/// <summary> /// 获得所有的文档库列表 /// </summary> /// <returns></returns> public string GetCoworkDocumentSiteList() { string queryApi = "lists?$filter=BaseTemplate eq 101&$select=" + new Document.Core.Model.DocumentSite().ToString(); return RequestHelper.RequestGet(_userName, _appUrl, queryApi, _documentType); }
下载:通过api从sharepoint服务器下载文件到iis服务器,然后返回该文件的在iis服务器的相对路径。当然可以直接返回byte[],这里不知道为什么要有该操作也是比较迷惑的地方,api提供的方式完全可以返回byte[]
/// <summary> /// 下载文件 /// </summary> /// <param name="serverRelativeUrl">文件在服务端的相对路径</param> /// <param name="fileLength"></param> /// <returns></returns> public string DownLoadFile(string serverRelativeUrl, int fileLength) { string loadApi = "GetFileByServerRelativeUrl('" + serverRelativeUrl + "')/openbinarystream"; string strJson = string.Empty; try { string saveServerRelativeUrl = "DownLoad\\" + _userName + "\\"; string filePath = AppDomain.CurrentDomain.BaseDirectory + saveServerRelativeUrl; string fileName = serverRelativeUrl.Substring(serverRelativeUrl.LastIndexOf('/') + 1); //"Personal\\chenwd\\Documents\\Shared with Everyone\\SharePoint" string relativeDir = serverRelativeUrl.Substring(0, serverRelativeUrl .LastIndexOf('/')) .Replace('/', '\\') .TrimStart("\\".ToCharArray()); filePath = filePath + relativeDir; if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } filePath = filePath + "\\" + fileName; if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } RequestHelper.RequestGet(_appUrl, loadApi, filePath, fileLength, _documentType); saveServerRelativeUrl = "\\" + saveServerRelativeUrl + relativeDir + "\\" + fileName; saveServerRelativeUrl = saveServerRelativeUrl.Replace('\\', '/'); string strFolderJson = string.Empty; strJson = strFolderJson.ToDownLoadJson("200", saveServerRelativeUrl); } catch (WebException ex) { strJson = string.Empty.ToErrorJson("500", ex.Message); } return strJson; }
上传文件是最头疼的地方,在网上查找了很多方法,最后是以下面的方式操作的。
/// <summary> /// 上传文件 /// </summary> /// <param name="serverReleativeUrl">文件夹的相对路径</param> /// <param name="fileName"></param> /// <returns>返回下载地址</returns> public string UploadFile(string serverReleativeUrl, string fileName, byte[] data, bool bOverWrite) { ClientContext spContext = new ClientContext(_appUrl); //认证方式 spContext.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication; Web website = spContext.Web; Folder folder = website.GetFolderByServerRelativeUrl(serverReleativeUrl); try { FileCreationInformation file = new FileCreationInformation(); file.Content = data; file.Url = fileName; file.Overwrite = bOverWrite; folder.Files.Add(file); spContext.ExecuteQuery(); } catch (Exception ex) { return string.Empty.ToErrorJson("500", ex.Message); } return string.Empty.ToSuccessJson("200", "上传成功"); }
拷贝
public string CopyTo(string soureRelativeUrl, string desRelativeUrl) { string strCopyApi = "GetFileByServerRelativeUrl('" + soureRelativeUrl + "')/copyto(strnewurl='" + desRelativeUrl + "',boverwrite=false)"; if (_documentType == Model.DocumentType.Personal) { RequestHelper.ClientID = _dicMapping[_userName]; } return RequestHelper.RequestPost(_userName, _appUrl, strCopyApi, _documentType); }
总结
在写接口的时候,在文件上传的地方,卡了一天的时间,也就是昨天的时候,突然查询了Miscrosoft.SharePoint.Client下的File类,才在网上看到那种实现方式,在群里也问了一些搞sharepoint的朋友,说那种方式操作起来更简单一些,如果用Msdn中提到的那种方式使用Post提交数据,需要在请求头中加一些参数。试了很多次无果,不得不放弃了。
下面是一些参考文章,希望对你有所帮助
https://msdn.microsoft.com/zh-SG/library/fp142385 OData相关
https://msdn.microsoft.com/zh-cn/library/jj164022.aspx
使用ClientObjectModel访问SharePoint数据
http://my.oschina.net/edens/blog/115601
转载地址:http://www.cnblogs.com/wolf-sun/p/4417155.html