• SharePoint 2010 自定义Ribbon实现文档批量下载为Zip文件


    在SharePoint 2010文档库中,结合单选框,在Ribbon中提供了批量处理文档的功能,比如,批量删除、批量签出、批量签入等,但是,很遗憾,没有提供批量下载,默认的只能一个个下载,当选择多个文档时,下载副本就变成了灰色不可用。

    在此我们将开发一个Ribbon按钮,实现文档(包括含有文件夹)的批量下载为Zip压缩包的功能。

    项目资源结构图

    先上传一张项目的资源管理结构

    现在开始:打开VS2010,创建一个“空白SharePoint项目”命名DeviantPoint.DownloadZip

    创建空白SharePoint项目

    指定用于部署的网站

    指定用于部署的网站

    然后添加一个到ICSharpCode.SharpZipLib.dll(一个.NET类库,用于处理Zip文件)的引用,然后创建一个文件夹Classes用于保存帮助类。

    创建类ZipBuilder,该类用于创建zip文件,创建这个帮助类可以使得在别的地方重复使用,而不仅仅针对本项目。
    通常,一个类的实例被创建后,你需要转递一个文件流写入zip文件。它可以是任意类型文件流,二进制流,存储流等等。在这个ZipBuilder类中允许你添加文件和文件夹,最终输出到zip文件。因为它要处理文件流,所以该类实现了IDisposable接口。
    如下为ZipBuilder类的代码:

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Linq;  
    4. using System.Text;  
    5. using System.IO;  
    6. using ICSharpCode.SharpZipLib.Zip;  
    7. using ICSharpCode.SharpZipLib.Core;  
    8.  
    9. namespace DeviantPoint.DownloadZip  
    10. {  
    11.     public class ZipFileBuilder : IDisposable  
    12.     {  
    13.         private bool disposed = false;  
    14.  
    15.         ZipOutputStream zipStream = null;  
    16.         protected ZipOutputStream ZipStream  
    17.         {  
    18.             get { return zipStream; }  
    19.  
    20.         }  
    21.  
    22.         ZipEntryFactory factory = null;  
    23.         private ZipEntryFactory Factory  
    24.         {  
    25.             get { return factory; }  
    26.         }  
    27.  
    28.  
    29.         public ZipFileBuilder(Stream outStream)  
    30.         {  
    31.             zipStream = new ZipOutputStream(outStream);  
    32.             zipStream.SetLevel(9); //best compression  
    33.  
    34.             factory = new ZipEntryFactory(DateTime.Now);  
    35.         }  
    36.  
    37.         public void Add(string fileName, Stream fileStream)  
    38.         {  
    39.             //create a new zip entry              
    40.             ZipEntry entry = factory.MakeFileEntry(fileName);  
    41.             entry.DateTime = DateTime.Now;  
    42.             ZipStream.PutNextEntry(entry);  
    43.  
    44.             byte[] buffer = new byte[65536];  
    45.  
    46.             int sourceBytes;  
    47.             do 
    48.             {  
    49.                 sourceBytes = fileStream.Read(buffer, 0, buffer.Length);  
    50.                 ZipStream.Write(buffer, 0, sourceBytes);  
    51.             }  
    52.             while (sourceBytes > 0);  
    53.  
    54.  
    55.         }  
    56.  
    57.         public void AddDirectory(string directoryName)  
    58.         {  
    59.             ZipEntry entry = factory.MakeDirectoryEntry(directoryName);  
    60.             ZipStream.PutNextEntry(entry);  
    61.         }  
    62.  
    63.         public void Finish()  
    64.         {  
    65.             if (!ZipStream.IsFinished)  
    66.             {  
    67.                 ZipStream.Finish();  
    68.             }  
    69.         }  
    70.  
    71.         public void Close()  
    72.         {  
    73.             Dispose(true);  
    74.             GC.SuppressFinalize(this);  
    75.         }  
    76.  
    77.         public void Dispose()  
    78.         {  
    79.             this.Close();  
    80.         }  
    81.  
    82.         protected virtual void Dispose(bool disposing)  
    83.         {  
    84.             if (!disposed)  
    85.             {  
    86.                 if (disposing)  
    87.                 {  
    88.                     if (ZipStream != null)  
    89.                         ZipStream.Dispose();  
    90.                 }  
    91.             }  
    92.  
    93.             disposed = true;  
    94.         }  
    95.     }  
    96. }  

    接下来,写一个SPExtensions.cs的类,用于向Microsoft.SharePoint对象添加一些扩展方法。这个类基本就是添加一些简单的方法到SPListItem类和SPList类。针对SPListItem类,我添加了一个方法用于判断是否SPListItem实例是一个文件夹,SPList类用于判断这个列表仅仅是一个文档。

    SPExtensions的代码如下: 
    SPExtensions.cs 
     

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Linq;  
    4. using System.Text;  
    5. using System.Runtime.CompilerServices;  
    6. using Microsoft.SharePoint;  
    7.  
    8. namespace DeviantPoint.DownloadZip  
    9. {  
    10.     public static class SPExtensions  
    11.     {  
    12.         public static bool IsFolder(this SPListItem item)  
    13.         {  
    14.             return (item.Folder != null);  
    15.         }  
    16.  
    17.         public static bool IsDocumentLibrary(this SPList list)  
    18.         {  
    19.             return (list.BaseType == SPBaseType.DocumentLibrary);  
    20.         }  
    21.     }  
    22. }  

    接下来要做的是添加一个“SharePoint映射文件夹”到项目,映射到Layouts目录(位于SharePoint根目录下)。在添加完SharePoint的“Layouts”映射文件夹后,我添加了一个SharePoint 2010 应该程序页DownloadZip.aspx到DeviantPoint.DownloadZip子文件夹,该页面负责处理来自客户端的请求并生成对应的zip文件返回到客户端。它的功能如同已知的在SharePoint 2010 Ribbon菜单中的“下载副本”的功能。来自客户端的POST请求会被发送到我的DownloadZip.aspx页面,然后这个页面把一些文档进行打包压缩到一个zip文件然后发送到客户端浏览器。这个页面需要两个参数:

    • sourceUrl –完整的文档(文件夹,包含子文件夹),请求的来源 
    • itemIDs – 列表项ID ,用分号隔开的一个SPListItem ID项目集,作为zip文件的一部分。注意:一个文件夹也有IDs如果一个文件夹被选中的话,自然其ID值也会被传递。 
    该应用程序页的基本功能是根据传递过来的id检索对应的SharePoint中的文档项,使用ZipBuilder 类把检索到的文档打包成一个zip文件。如果是一个文件夹的id,则会创建文件夹(最终保存到zip文件中),并依次检索文件夹下的所有文件

    下面是DownloadZip.aspx 应用程序页的后置代码 :

    1. using System;  
    2. using System.IO;  
    3. using System.Web;  
    4. using Microsoft.SharePoint;  
    5. using Microsoft.SharePoint.WebControls;  
    6. using ICSharpCode.SharpZipLib.Zip;  
    7.  
    8. namespace DeviantPoint.DownloadZip.Layouts.DeviantPoint.DownloadZip  
    9. {  
    10.     public partial class DownloadZip : LayoutsPageBase  
    11.     {  
    12.         protected void Page_Load(object sender, EventArgs e)  
    13.         {  
    14.             string fullDocLibSourceUrl = Request.Params["sourceUrl"];  
    15.             if (string.IsNullOrEmpty(fullDocLibSourceUrl)) return;  
    16.  
    17.             string docLibUrl = fullDocLibSourceUrl.Replace(SPContext.Current.Site.Url, "");  
    18.  
    19.             SPList list = SPContext.Current.Web.GetList(docLibUrl);  
    20.             if (!list.IsDocumentLibrary()) return;  
    21.  
    22.             string pItemIds = Request.Params["itemIDs"];  
    23.             if (string.IsNullOrEmpty(pItemIds)) return;  
    24.  
    25.             SPDocumentLibrary library = (SPDocumentLibrary)list;  
    26.  
    27.             string[] sItemIds = pItemIds.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);  
    28.             int[] itemsIDs = new int[sItemIds.Length];  
    29.             for (int i = 0; i < sItemIds.Length; i++)  
    30.             {  
    31.                 itemsIDs[i] = Convert.ToInt32(sItemIds[i]);  
    32.             }  
    33.  
    34.             if (itemsIDs.Length > 0)  
    35.             {  
    36.                 using (MemoryStream ms = new MemoryStream())  
    37.                 {  
    38.                     using (ZipFileBuilder builder = new ZipFileBuilder(ms))  
    39.                     {  
    40.                         foreach (int id in itemsIDs)  
    41.                         {  
    42.                             SPListItem item = library.GetItemById(id);  
    43.                             if (item.IsFolder())  
    44.                                 AddFolder(builder, item.Folder, string.Empty);  
    45.                             else 
    46.                                 AddFile(builder, item.File, string.Empty);  
    47.                         }  
    48.  
    49.                         builder.Finish();  
    50.                         WriteStreamToResponse(ms);  
    51.                     }  
    52.                 }  
    53.             }  
    54.  
    55.         }  
    56.  
    57.         private static void AddFile(ZipFileBuilder builder, SPFile file, string folder)  
    58.         {  
    59.             using (Stream fileStream = file.OpenBinaryStream())  
    60.             {  
    61.                 builder.Add(folder + "\\" + file.Name, fileStream);  
    62.                 fileStream.Close();  
    63.             }  
    64.         }  
    65.  
    66.         private void AddFolder(ZipFileBuilder builder, SPFolder folder, string parentFolder)  
    67.         {  
    68.             string folderPath = parentFolder == string.Empty ? folder.Name : parentFolder + "\\" + folder.Name;  
    69.             builder.AddDirectory(folderPath);  
    70.  
    71.             foreach (SPFile file in folder.Files)  
    72.             {  
    73.                 AddFile(builder, file, folderPath);  
    74.             }  
    75.  
    76.             foreach (SPFolder subFolder in folder.SubFolders)  
    77.             {  
    78.                 AddFolder(builder, subFolder, folderPath);  
    79.             }  
    80.         }  
    81.  
    82.         private void WriteStreamToResponse(MemoryStream ms)  
    83.         {  
    84.             if (ms.Length > 0)  
    85.             {  
    86.                 string filename = DateTime.Now.ToFileTime().ToString() + ".zip";  
    87.                 Response.Clear();  
    88.                 Response.ClearHeaders();  
    89.                 Response.ClearContent();  
    90.                 Response.AddHeader("Content-Length", ms.Length.ToString());  
    91.                 Response.AddHeader("Content-Disposition""attachment; filename=" + filename);  
    92.                 Response.ContentType = "application/octet-stream";  
    93.  
    94.                 byte[] buffer = new byte[65536];  
    95.                 ms.Position = 0;  
    96.                 int num;  
    97.                 do 
    98.                 {  
    99.                     num = ms.Read(buffer, 0, buffer.Length);  
    100.                     Response.OutputStream.Write(buffer, 0, num);  
    101.                 }  
    102.  
    103.                 while (num > 0);  
    104.  
    105.                 Response.Flush();  
    106.             }  
    107.         }  
    108.     }  
    109. }  

    在创建完应用程序页后,我添加一个SharePoint 2010 空白元素DownloadZip到项目中,打开Elements.xml

    代码如下

    1. <?xml version="1.0" encoding="utf-8"?> 
    2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
    3.     <CustomAction Id="DeviantPoint.DownloadZip" Location="CommandUI.Ribbon"> 
    4.         <CommandUIExtension> 
    5.             <CommandUIDefinitions> 
    6.                 <CommandUIDefinition Location="Ribbon.Documents.Copies.Controls._children"> 
    7.                     <Button Id="Ribbon.Documents.Copies.DownloadZip" 
    8.                             Command="DownloadZip" 
    9.                             Sequence="15" 
    10.                             Image16by16="/_layouts/images/DeviantPoint.DownloadZip/zip_16x16.png" 
    11.                             Image32by32="/_layouts/images/DeviantPoint.DownloadZip/zip_32x32.png" 
    12.                             Description="Download zip" LabelText="Download as Zip" 
    13.                             TemplateAlias="o1"/> 
    14.                 </CommandUIDefinition> 
    15.             </CommandUIDefinitions> 
    16.             <CommandUIHandlers> 
    17.                 <CommandUIHandler 
    18.                   Command="DownloadZip" 
    19.                   CommandAction="javascript:downloadZip();" 
    20.                   EnabledScript="javascript:enable();"/> 
    21.             </CommandUIHandlers> 
    22.         </CommandUIExtension> 
    23.     </CustomAction> 
    24.     <CustomAction Id="Ribbon.Library.Actions.Scripts" 
    25.                   Location="ScriptLink" 
    26.                   ScriptSrc="/_layouts/DeviantPoint.DownloadZip/CustomActions.js" /> 
    27. </Elements> 

    对此CommandUIDefinition, 我设置 Location attribute 为"Ribbon.Documents.Copies.Controls._children”. 我还希望它能够显示在“下载副本”功能的右边,因此需要设置按钮元素序列号属性,设置为15(这个“下载副本按钮的序列号为10,发送到按钮的序列号为20,自然我需要设置它的序列号为它们之间),想要知道每个功能按钮的序列号你可能查看C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\GLOBAL\XML\CMDUI.xml 文件。我同时也指定了功能按钮的图像(创建一个SharePoint的“Images”映射文件夹),同时指定TemplateAlias为“o1”以便我的图标显示的大些,如同“下载副本”的一样。我也定义按钮的command处理事件在Elements.xml文件中通过添加一个CommandUIHandler元素。这CommandAction 属性用于指定按钮被期望的动作,EnabledScript属性用于决定是否按钮功能被启用。这两个属性值都指向一个javascript函数(放在一个单独的文件中,稍后讨论)因为我使用了一个单独的javascript文件,我还必须在Elements文件中添加另一个CustomAction元素,用以指名我的javascript文件的位置。

    最后,创建CustomActions.js文件,这个文件用于定义ribbon按钮的功能/行为。enable()函数用于决定按钮是否可用。如果至少有一个文档项被选中的话,那么我的按钮就是可用的。 downloadZip() 函数用于开始下载过程

    如下为CustomActions.js 的源码:

    1. function enable() {  
    2.     var items = SP.ListOperation.Selection.getSelectedItems();  
    3.     var itemCount = CountDictionary(items);  
    4.     return (itemCount > 0);  
    5.  
    6. }  
    7.  
    8. function downloadZip() {  
    9.  
    10.     var context = SP.ClientContext.get_current();  
    11.     this.site = context.get_site();  
    12.     this.web = context.get_web();  
    13.     context.load(this.site);  
    14.     context.load(this.web);  
    15.     context.executeQueryAsync(  
    16.         Function.createDelegate(thisthis.onQuerySucceeded),  
    17.         Function.createDelegate(thisthis.onQueryFailed)  
    18.     );  
    19. }  
    20.  
    21. function onQuerySucceeded() {  
    22.  
    23.     var items = SP.ListOperation.Selection.getSelectedItems();  
    24.     var itemCount = CountDictionary(items);  
    25.  
    26.     if (itemCount == 0) return;  
    27.  
    28.     var ids = "";  
    29.     for (var i = 0; i < itemCount; i++) {  
    30.         ids += items[i].id + ";";  
    31.     }  
    32.  
    33.     //send a request to the zip aspx page.  
    34.     var form = document.createElement("form");  
    35.     form.setAttribute("method""post");  
    36.     form.setAttribute("action"this.site.get_url() + this.web.get_serverRelativeUrl() + "/_layouts/deviantpoint.downloadzip/downloadzip.aspx");  
    37.  
    38.     var hfSourceUrl = document.createElement("input");  
    39.     hfSourceUrl.setAttribute("type""hidden");  
    40.     hfSourceUrl.setAttribute("name""sourceUrl");  
    41.     hfSourceUrl.setAttribute("value", location.href);  
    42.     form.appendChild(hfSourceUrl);  
    43.  
    44.     var hfItemIds = document.createElement("input")  
    45.     hfItemIds.setAttribute("type""hidden");  
    46.     hfItemIds.setAttribute("name""itemIDs");  
    47.     hfItemIds.setAttribute("value", ids);  
    48.     form.appendChild(hfItemIds);  
    49.  
    50.     document.body.appendChild(form);  
    51.     form.submit();  
    52. }  
    53.  
    54. function onQueryFailed(sender, args) {  
    55.     this.statusID = SP.UI.Status.addStatus("Download as Zip:",  
    56.         "Downloading Failed: " + args.get_message() + " <a href='#' onclick='javascript:closeStatus();return false;'>Close</a>."true);  
    57.     SP.UI.Status.setStatusPriColor(this.statusID, "red");  
    58. }  
    59.  
    60. function closeStatus() {  
    61.     SP.UI.Status.removeStatus(this.statusID);  

    到此结束,生成并部署,即可实现文档(文件夹)的批量下载为压缩包。

    批量下载为压缩包

    如果你出现“找不到文件的错误”,请看下一篇文章,这是因为没有把ICSharpCode.SharpZipLib.dll注册到GAC的原因。

    本文翻译于:http://www.deviantpoint.com/post/2010/05/08/SharePoint-2010-Download-as-Zip-File-Custom-Ribbon-Action.aspx

  • 相关阅读:
    家庭记账本(三)记录页面的完善
    家庭记账本(二)记录界面显示
    家庭记账本(一)页面绘制
    MyBaits增删改查
    2021.01.20
    Maven_依赖与生命周期
    1592. Rearrange Spaces Between Words
    1588. Sum of All Odd Length Subarrays
    933. Number of Recent Calls
    765. Couples Holding Hands
  • 原文地址:https://www.cnblogs.com/blackbean/p/2616889.html
Copyright © 2020-2023  润新知