在SharePoint 2010文檔庫中,結合單選框,在Ribbon中提供了批量處理文檔的功能,比如,批量刪除、批量簽出、批量簽入等,但是,很遺憾,沒有提供批量下載,默認的只能一個個下載,當選擇多個文檔時,下載副本就變成了灰色不可用。
在此我們將開發一個Ribbon按鈕,實現文檔(包括含有文件夾)的批量下載爲Zip壓縮包的功能。
先上傳一張項目的資源管理結構
現在開始:打開VS2010,創建一個“空白SharePoint項目”命名DeviantPoint.DownloadZip
指定用於部署的網站
然後添加一個到ICSharpCode.SharpZipLib.dll(一個.NET類庫,用於處理Zip文件)的引用,然後創建一個文件夾Classes用於保存幫助類。
創建類ZipBuilder,該類用於創建zip文件,創建這個幫助類可以使得在別的地方重複使用,而不僅僅針對本項目。
通常,一個類的實例被創建後,你需要轉遞一個文件流寫入zip文件。它可以是任意類型文件流,二進制流,存儲流等等。在這個ZipBuilder類中允許你添加文件和文件夾,最終輸出到zip文件。因爲它要處理文件流,所以該類實現了IDisposable接口。
如下爲ZipBuilder類的代碼:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
- using ICSharpCode.SharpZipLib.Zip;
- using ICSharpCode.SharpZipLib.Core;
- namespace DeviantPoint.DownloadZip
- {
- public class ZipFileBuilder : IDisposable
- {
- private bool disposed = false;
- ZipOutputStream zipStream = null;
- protected ZipOutputStream ZipStream
- {
- get { return zipStream; }
- }
- ZipEntryFactory factory = null;
- private ZipEntryFactory Factory
- {
- get { return factory; }
- }
- public ZipFileBuilder(Stream outStream)
- {
- zipStream = new ZipOutputStream(outStream);
- zipStream.SetLevel(9); //best compression
- factory = new ZipEntryFactory(DateTime.Now);
- }
- public void Add(string fileName, Stream fileStream)
- {
- //create a new zip entry
- ZipEntry entry = factory.MakeFileEntry(fileName);
- entry.DateTime = DateTime.Now;
- ZipStream.PutNextEntry(entry);
- byte[] buffer = new byte[65536];
- int sourceBytes;
- do
- {
- sourceBytes = fileStream.Read(buffer, 0, buffer.Length);
- ZipStream.Write(buffer, 0, sourceBytes);
- }
- while (sourceBytes > 0);
- }
- public void AddDirectory(string directoryName)
- {
- ZipEntry entry = factory.MakeDirectoryEntry(directoryName);
- ZipStream.PutNextEntry(entry);
- }
- public void Finish()
- {
- if (!ZipStream.IsFinished)
- {
- ZipStream.Finish();
- }
- }
- public void Close()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- public void Dispose()
- {
- this.Close();
- }
- protected virtual void Dispose(bool disposing)
- {
- if (!disposed)
- {
- if (disposing)
- {
- if (ZipStream != null)
- ZipStream.Dispose();
- }
- }
- disposed = true;
- }
- }
- }
接下來,寫一個SPExtensions.cs的類,用於向Microsoft.SharePoint對象添加一些擴展方法。這個類基本就是添加一些簡單的方法到SPListItem類和SPList類。針對SPListItem類,我添加了一個方法用於判斷是否SPListItem實例是一個文件夾,SPList類用於判斷這個列表僅僅是一個文檔。
SPExtensions的代碼如下:
SPExtensions.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Runtime.CompilerServices;
- using Microsoft.SharePoint;
- namespace DeviantPoint.DownloadZip
- {
- public static class SPExtensions
- {
- public static bool IsFolder(this SPListItem item)
- {
- return (item.Folder != null);
- }
- public static bool IsDocumentLibrary(this SPList list)
- {
- return (list.BaseType == SPBaseType.DocumentLibrary);
- }
- }
- }
接下來要做的是添加一個“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 應用程序頁的後置代碼 :
- using System;
- using System.IO;
- using System.Web;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.WebControls;
- using ICSharpCode.SharpZipLib.Zip;
- namespace DeviantPoint.DownloadZip.Layouts.DeviantPoint.DownloadZip
- {
- public partial class DownloadZip : LayoutsPageBase
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- string fullDocLibSourceUrl = Request.Params["sourceUrl"];
- if (string.IsNullOrEmpty(fullDocLibSourceUrl)) return;
- string docLibUrl = fullDocLibSourceUrl.Replace(SPContext.Current.Site.Url, "");
- SPList list = SPContext.Current.Web.GetList(docLibUrl);
- if (!list.IsDocumentLibrary()) return;
- string pItemIds = Request.Params["itemIDs"];
- if (string.IsNullOrEmpty(pItemIds)) return;
- SPDocumentLibrary library = (SPDocumentLibrary)list;
- string[] sItemIds = pItemIds.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
- int[] itemsIDs = new int[sItemIds.Length];
- for (int i = 0; i < sItemIds.Length; i++)
- {
- itemsIDs[i] = Convert.ToInt32(sItemIds[i]);
- }
- if (itemsIDs.Length > 0)
- {
- using (MemoryStream ms = new MemoryStream())
- {
- using (ZipFileBuilder builder = new ZipFileBuilder(ms))
- {
- foreach (int id in itemsIDs)
- {
- SPListItem item = library.GetItemById(id);
- if (item.IsFolder())
- AddFolder(builder, item.Folder, string.Empty);
- else
- AddFile(builder, item.File, string.Empty);
- }
- builder.Finish();
- WriteStreamToResponse(ms);
- }
- }
- }
- }
- private static void AddFile(ZipFileBuilder builder, SPFile file, string folder)
- {
- using (Stream fileStream = file.OpenBinaryStream())
- {
- builder.Add(folder + "\\" + file.Name, fileStream);
- fileStream.Close();
- }
- }
- private void AddFolder(ZipFileBuilder builder, SPFolder folder, string parentFolder)
- {
- string folderPath = parentFolder == string.Empty ? folder.Name : parentFolder + "\\" + folder.Name;
- builder.AddDirectory(folderPath);
- foreach (SPFile file in folder.Files)
- {
- AddFile(builder, file, folderPath);
- }
- foreach (SPFolder subFolder in folder.SubFolders)
- {
- AddFolder(builder, subFolder, folderPath);
- }
- }
- private void WriteStreamToResponse(MemoryStream ms)
- {
- if (ms.Length > 0)
- {
- string filename = DateTime.Now.ToFileTime().ToString() + ".zip";
- Response.Clear();
- Response.ClearHeaders();
- Response.ClearContent();
- Response.AddHeader("Content-Length", ms.Length.ToString());
- Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
- Response.ContentType = "application/octet-stream";
- byte[] buffer = new byte[65536];
- ms.Position = 0;
- int num;
- do
- {
- num = ms.Read(buffer, 0, buffer.Length);
- Response.OutputStream.Write(buffer, 0, num);
- }
- while (num > 0);
- Response.Flush();
- }
- }
- }
- }
在創建完應用程序頁後,我添加一個SharePoint 2010 空白元素DownloadZip到項目中,打開Elements.xml
代碼如下
- <?xml version="1.0" encoding="utf-8"?>
- <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
- <CustomAction Id="DeviantPoint.DownloadZip" Location="CommandUI.Ribbon">
- <CommandUIExtension>
- <CommandUIDefinitions>
- <CommandUIDefinition Location="Ribbon.Documents.Copies.Controls._children">
- <Button Id="Ribbon.Documents.Copies.DownloadZip"
- Command="DownloadZip"
- Sequence="15"
- Image16by16="/_layouts/images/DeviantPoint.DownloadZip/zip_16x16.png"
- Image32by32="/_layouts/images/DeviantPoint.DownloadZip/zip_32x32.png"
- Description="Download zip" LabelText="Download as Zip"
- TemplateAlias="o1"/>
- </CommandUIDefinition>
- </CommandUIDefinitions>
- <CommandUIHandlers>
- <CommandUIHandler
- Command="DownloadZip"
- CommandAction="javascript:downloadZip();"
- EnabledScript="javascript:enable();"/>
- </CommandUIHandlers>
- </CommandUIExtension>
- </CustomAction>
- <CustomAction Id="Ribbon.Library.Actions.Scripts"
- Location="ScriptLink"
- ScriptSrc="/_layouts/DeviantPoint.DownloadZip/CustomActions.js" />
- </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 的源碼:
- function enable() {
- var items = SP.ListOperation.Selection.getSelectedItems();
- var itemCount = CountDictionary(items);
- return (itemCount > 0);
- }
- function downloadZip() {
- var context = SP.ClientContext.get_current();
- this.site = context.get_site();
- this.web = context.get_web();
- context.load(this.site);
- context.load(this.web);
- context.executeQueryAsync(
- Function.createDelegate(this, this.onQuerySucceeded),
- Function.createDelegate(this, this.onQueryFailed)
- );
- }
- function onQuerySucceeded() {
- var items = SP.ListOperation.Selection.getSelectedItems();
- var itemCount = CountDictionary(items);
- if (itemCount == 0) return;
- var ids = "";
- for (var i = 0; i < itemCount; i++) {
- ids += items[i].id + ";";
- }
- //send a request to the zip aspx page.
- var form = document.createElement("form");
- form.setAttribute("method", "post");
- form.setAttribute("action", this.site.get_url() + this.web.get_serverRelativeUrl() + "/_layouts/deviantpoint.downloadzip/downloadzip.aspx");
- var hfSourceUrl = document.createElement("input");
- hfSourceUrl.setAttribute("type", "hidden");
- hfSourceUrl.setAttribute("name", "sourceUrl");
- hfSourceUrl.setAttribute("value", location.href);
- form.appendChild(hfSourceUrl);
- var hfItemIds = document.createElement("input")
- hfItemIds.setAttribute("type", "hidden");
- hfItemIds.setAttribute("name", "itemIDs");
- hfItemIds.setAttribute("value", ids);
- form.appendChild(hfItemIds);
- document.body.appendChild(form);
- form.submit();
- }
- function onQueryFailed(sender, args) {
- this.statusID = SP.UI.Status.addStatus("Download as Zip:",
- "Downloading Failed: " + args.get_message() + " <a href='#' onclick='javascript:closeStatus();return false;'>Close</a>.", true);
- SP.UI.Status.setStatusPriColor(this.statusID, "red");
- }
- function closeStatus() {
- SP.UI.Status.removeStatus(this.statusID);
- }
到此結束,生成並部署,即可實現文檔(文件夾)的批量下載爲壓縮包。
如果你出現“找不到文件的錯誤”,請看下一篇文章,這是因爲沒有把ICSharpCode.SharpZipLib.dll註冊到GAC的原因。
本人新手,請大家多多關照,也歡迎與我進行技術溝通。QQ:906753030