Custom Ribbon Action (DownLoad Multiple File) -------------SharePoint 2010

http://www.cnblogs.com/KingStar/archive/2010/10/13/1850034.html

 

Below is a screenshot of my project

 

The first thing I did was to create a new SharePoint 2010 Empty SharePoint Project called DeviantPoint.DownloadZip. Then, I added a reference to ICSharpCode.SharpZipLib.dll which is a part of SharpZipLib (a .NET library that is well-used for working with Zip files). I then created a helper class, ZipBuilder, used to actually create the zip file. I created this helper class with the intent that I could reuse this elsewhere, not just for this project.

Basically, when an instance of this class is constructed, you need to pass in a stream that will be used to write the contents of the zip file to. This could be any type stream (FileStream, MemoryStream, etc). There are a couple of helper methods in this class that allow you to add files and folders and method that “finalizes” the zip file. This Finalize() method must always be called to ensure that the zip file is written out correctly. This class also implements the IDisposable pattern since it is handling streams.

This is the code for the ZipBuilder class:

 

代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.IO;
using  ICSharpCode.SharpZipLib.Zip;
using  ICSharpCode.SharpZipLib.Core;
namespace  SPMultipleFileDownLoad.Classes
{
    
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 ;
        }
    }
}

 

 

The next thing I wrote was SPExtensions.cs, a class for adding extension methods to some of the Microsoft.SharePoint objects. This class basically just adds a few simple helper methods to the SPListItem class and the SPList class. For the SPListItem class, I just added a method to determine if the SPListItem instance is actually a folder and for the SPList class, I added a method to determine if the list is actually a document library.

The code for SPExtensions is below:

 

 

代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

// Add 
using  System.Runtime.CompilerServices;
using  Microsoft.SharePoint;

namespace  SPMultipleFileDownLoad.Classes
{
   
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);
       }

    }
}

 

 

 

The next thing I did was to add a SharePoint Mapped Folder to my project mapping to the Layouts directory located in the SharePoint root. When you add a mapped folder, Visual Studio will automatically create a sub-folder in that mapped folder with the same name as your project. This is a good thing as you don’t want to be mixing up all your project files with all of the files that come out of the box from SharePoint.

After I had my Layouts mapped folder and subfolder created, I added a SharePoint 2010 Application Page item to the sub-folder called DownloadZip.aspx. The purpose of this application page is to actually handle the request from the client to build the zip file and send it back down to the client. Having an application page handle this is the same technique that is used with the ‘Download a Copy’ action button you see in the SharePoint 2010 ribbon. Basically, a POST request from a client is sent to my DownloadZip.aspx page and this page takes care of packaging up a zip file and sending it down to the client’s browser. This page expects two parameters:

  • sourceUrl –the full url of the document library (and folder, if inside of a subfolder) where the request is being made from
  • itemIDs – a semi-colon delimited list of the SPListItem IDs that should be included as part of the zip file. Note that folders also have ids so if a folder is selected, that folder’s id would also be sent.

The code-behind for this application page basically takes the list of item ids and for each item id, goes and grabs the corresponding file from the document library in SharePoint and, using the ZipBuilder class, packages it up as a zip file. If one of the items that was selected is actually a folder, it will create that folder in the zip file as well and put all the items that are in that SharePoint folder into the corresponding zip file folder. It will also traverse through all the sub-folders in the hierarchy.

Below is the code-behind for the DownloadZip.aspx application page (there is nothing I added to the Download.aspx file itself):

DownloadZip.aspx.cs

 

代码
using  System;
using  Microsoft.SharePoint;
using  Microsoft.SharePoint.WebControls;

using  System.IO;
using  System.Web;
using  SPMultipleFileDownLoad.Classes;


namespace  SPMultipleFileDownLoad.Layouts.SPMultipleFileDownLoad
{
    
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();

            }
        }
    }
}



 

 

After creating the application page, I added a SharePoint 2010 Empty Element item called DownloadZip to my project. This is nothing more than an Elements.xml file that takes care of adding my custom action to ribbon (CustomAction.Location=”CommandUI.Ribbon”). By default, for document libraries, this is what the ribbon looks like:

image

I wanted to add my action inside of the area in the Documents tab, inside of the Copies group so to do this, for the CommandUIDefinition, I set the Location attribute to "Ribbon.Documents.Copies.Controls._children”. I also wanted it to appear right after the Download a Copy action so for the Button element’s Sequence attribute, I set the value to 15 (the Download a Copy button has a sequence of 10 and the Send To button has a sequence of 20 so I needed to set the sequence of my button to something in between). To understand where everything is placed and what the sequences are by default, you need to look at the file C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/GLOBAL/XML/CMDUI.xml. I also specified the icons I wanted to use for my action (these icons are also part of my project, located in a sub-directory of the Images SharePoint mapped folder) and I also set the TemplateAlias to “o1” so that my icon shows up large like Download a Copy does. I also define the actual command handler in this Elements.xml file by adding a CommandUIHandler element. The CommandAction attribute is used to specify what exactly the button is supposed to do and the EnabledScript attribute is used to determine whether or not the button/command is enabled. These two attributes’ values both point to javascript functions I define in a separate file (discussed later). Because I’m using a separate javascript file, I also have to add another CustomAction element in the Elements file that points to the location of my javascript file. This is the result:

Enabled

image

Disabled

image

Below is the full Elements.xml file:

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/SPMultipleFileDownLoad/zip.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/SPMultipleFileDownLoad/CustomActions.js"   />
</ Elements >

 

 

Finally, I created the CustomActions.js file. This file is used to define the actions/behavior of my new ribbon button. The enable() function is used to determine whether or not my button is enabled. If there is at least one item selected, then my button is enabled. The downloadZip() function just starts off the download process. Actually, I could have probably written the javascript so I didn’t even need this function or calls to SP.ClientContext.executeQueryAsync() but I was just trying to get something done quickly and actually writing it this way gave me another place to show-off another one of the UI features, the Status. If the call to SP.ClientContext.executeQueryAsync() fails, then the onQueryFailed delegate is executed. The onQueryFailed() function uses the SP.UI.Status to display the error message, shown here:

download_failed

The function onQuerySucceeded() is where the majority of the action happens. I use the SP.ListOperation.Selection object to get a list of the selected items. I then create a request to my DownloadZip.aspx application page and send that page the list of selected item ids as well as the current url (the url of the page the user is on). Like I said earlier, that application page takes care of packaging everything up as a zip and streaming it down to the browser.

Below is the code for CustomActions.js:

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/SPMultipleFileDownLoad/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='#' οnclick='javascript:closeStatus();return false;'>Close</a>. " true );  
            SP.UI.Status.setStatusPriColor(
this .statusID,  " red " );  
      }   

 

 

So how does this actually all look when the user is using it? Below is the hierarchy of an example document library I have:

image

  • Documents
    • Folder A (Folder)
      • Subfolder in Folder A (Folder)
        • Sub Sub Folder (Folder)
          • Versioning Flow (Visio diagram)
        • Business Brief_SoW (Word document)
        • SoW_Phase1 (Word document)
      • Request Email (Text file)
      • Users and Roles (Excel file)
    • Issues (Excel file)
    • Product_Planning (Excel file)

The user has selected some documents and a sub-folder so my custom ribbon button is enabled:

image

The user clicks on this button and this feature executes and after it’s complete the user is prompted with this (note, the filename is a timestamp):

image

 

Reference :http://www.deviantpoint.com/post/2010/05/08/SharePoint-2010-Download-as-Zip-File-Custom-Ribbon-Action.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值