2009-03-31

How To: Download any Version of a Document in a SharePoint Document LIbrary

Following up my earlier post about SharePoint Document Libraries, it's also very useful to be able to download any version of a file in a Document Library in SharePoint. If you're using SharePoint, there's very little reason not to use versioning, and darned it, you can make use of it very easily as a .NET developer.

Alright... First, add a Web Reference, just like we did in the previous post, to one of the SharePoint Web Services, this time, we'll be hitting Versions.asmx, found here: http://SharePointMachineName/_vti_bin/Versions.asmx

Next up, add a couple classes to your code somewhere...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;

/// <summary>
/// Summary description for FileVersion
/// </summary>
public class FileVersion
{
    public FileVersion()
    {
    }
    public void MapData(DataRow row)
    {
        if (row["version"] != null)
        {            version = row["version"].ToString();        }
        if (row["url"] != null)
        {            url = row["url"].ToString();        }
        if (row["created"] != null)
        {            created = DateTime.Parse(row["created"].ToString());        }
        if (row["createdBy"] != null)
        {            createdBy = row["createdBy"].ToString();        }
        if (row["size"] != null)
        {            size = long.Parse( row["size"].ToString());        }
        if (row["comments"] != null)
        {            comments = row["comments"].ToString();        }
    }

    public string version    {        get;        set;    }
    public string url    {        get;        set;    }
    public DateTime created    {        get;        set;    }
    public string createdBy    {        get;        set;    }
    public long size    {        get;        set;    }
    public string comments    {        get;        set;    }
}

public class Files : List<FileVersion>
{
    public void MapData(DataTable dt)
    {
        foreach (DataRow dr in dt.Rows)
        {
            FileVersion fv = new FileVersion();
            try
            {
                fv.MapData(dr);
                if (fv.url != string.Empty)
                {
                    this.Add(fv);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}

These two basic classes to map our returned File and/or file Versions back to. MapData is a way to take a DataRow and map it to our object. You'll notice I'm using .NET Generics, and the shorthand getters/setters introduced in .NET 3.0.

Alright, next up, if you're following yesterday's example, add some code to our DocHelperLib. If not, find a good place for this block of code:

public static Files VersionsGetVersions(string SharePointHost, string UserName, string Password, string Domain, string FileName)
{
     // proxy object to call the Versions web service
     Versions VersionsService = new Versions();

     // the user credentials to use
     VersionsService.Credentials = new NetworkCredential(UserName, Password, Domain);
     VersionsService.Url = SharePointHost + "_vti_bin/Versions.asmx";

     // gets the file versions
     XmlNode Result = VersionsService.GetVersions(FileName);

     // dispose the web service object
     VersionsService.Dispose();

     Files f = new Files();
     DataSet ds = new DataSet();
     StringReader reader = new StringReader(Result.OuterXml);
     ds.ReadXml(reader);
     f.MapData(ds.Tables["result"]);
     return f;
}

You'll notice some helper code here to turn our raw string of Xml into an untyped DataSet. This is optional, but my business objects often work directly with DataSets. Also, on that note, MapData (first code block) is very similar to the method that we've written for bleak to MapData (although, ours is now Attribute based).

Finally, let's use this code somewhere. For my example, I've added this to the Page_Load method of our class, although, your use my vary.

protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            Files f = DocumentLibrary.VersionsGetVersions("http://SharePointMachineName/OptionalSharePointSiteName/", uname, pword, domain, "DocumentLibraryName/DocumentName.doc");
            foreach (FileVersion fv in f)
            {
                Response.Write("<a href=\"" + fv.url + "\">" + fv.version + "</a><br />");
            }
        }
        catch (Exception ex)
        {
            if (ex.InnerException != null)
            {
                Response.Write(ex.InnerException.Message);
            }
            Response.Write(ex.Message);
        }
    }

You'll have to get pass in your UserName, Password, and Domain information, but as with yesterday's example, you could use Windows Authentication from IIS and consume the Default Credentials that way, rather than passing in network username and passwords Also, make sure that you know where your SharePointMachineName, OpitonalSharePointSiteName, DocumentLibraryName, and DocumentName variables are coming from.

There you go. With this example, assuming you successfully authenticate to a valid SharePoint site, you should now have a list of SharePoint file versions in very crude Html at the top of your page. As appropriate, bind this to a datagrid, or use business logic to determine which version specific users need, etc. The fun you can  have with this is incredible. Hope this helps!

Zoidberg away!

3 comments:

Sohail Raza said...

Hi,

When I loop through all the versions, the current version is easily downloaded from the doc library but the older version returns null FieldInfo and null Stream.

Please advise

Betty said...

I have the same problem as Sohail. Any update?

urbanikt said...

Hi,
Could someone get through this problem. I can't download old version of file, too. I only have list of versions. I would be glad for some help.