Munavvar Husein Shaikh
Munavvar Husein Shaikh

Reputation: 583

How to download the documents from Version History from a document in SharePoint

I want to download all the documents from "Version History" of a file in SharePoint.

I need the documents either via an interface or a C# code.

I have also tried below API URL, but I am getting an error.

https://XXXX.sharepoint.com/_api/Web/GetFileByServerRelativeUrl('fileURL')/Versions

Upvotes: 0

Views: 4009

Answers (5)

Bruno Leitão
Bruno Leitão

Reputation: 821

I have a working solution for a migrating tool that I developed. The requisite is migrate file and older versions from a SharePoint environment to another one.

To download an older version do this:

public byte[] DownloadFile(string url, bool isHistory = false)
{
    bool isSharepointOnline = (SPCurrentContext.Credentials is SharePointOnlineCredentials);
    HttpResponseMessage response = null;
    byte[] buffer = null;

    if (!isHistory)
    {
        using (FileInformation fileInfo = File.OpenBinaryDirect(SPCurrentContext, url))
        using (IO.MemoryStream memory = new IO.MemoryStream())
        {
            fileInfo.Stream.CopyTo(memory);
            buffer = memory.ToArray();
        }
    }
    else if (isSharepointOnline)
    {
        using (WebClient httpClient = new WebClient())
        {
            httpClient.Credentials = SPCurrentContext.Credentials;
            httpClient.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
            httpClient.Headers["User-Agent"] = this.NONISV; // This is the TRICK!!! 

            buffer = httpClient.DownloadData(string.Concat(SPCurrentContext.Url, url));
        }
    }
    else
    {
        using (HttpClientHandler httpHandler = new HttpClientHandler() { Credentials = SPCurrentContext.Credentials })
        using (HttpClient client = new HttpClient(httpHandler))
        {
            response = client.GetAsync(string.Concat(SPCurrentContext.Url, url)).Result;

            response.EnsureSuccessStatusCode();

            response.Content.LoadIntoBufferAsync().Wait();

            buffer = response.Content.ReadAsByteArrayAsync().Result;
        }
    }

    return buffer;
}

Explanation:

This method works fine to download current version or older version of a file since you have file URL (File.ServerRelativeUrl and FileVersion.Url should help you with this step). Then, when you try to download file version from "SharePoint Online" as you do in "SP On-Premises", you will get 403 HTTP ERROR.

When you put some user-agent and X-FORMS_BASED_AUTH_ACCEPTED headers, the solution starts to work. But... Certainly you will receive a timeout error due to 429 HTTP ERROR. To bypass SharePoint Throttling, you have to register your program as a SharePoint Add-In.

To do this, go to:

{your sharepoint online root url}/_layouts/15/AppRegNew.aspx

And generate your Add-In registration (see this link and this link for instructions and more info)

Once you got it, you can use NONISV|{Your Company Name}|{Your Add-In Name}/1.0 as User-Agent HTTP Header. Good luck!

Upvotes: -1

TM000
TM000

Reputation: 1

This answer is for someone else who might be looking for a Powershell CSOM script which will download current documents plus versions history.

Admittedly a Powershell newbie, I struggled with finding a script which would download all current files and their versions from SharePoint. Accessing the versions proved to be a challenge as various scripts I tried either came up with (404) Not Found or (403) Forbidden. I was able to piece together a CSOM function which I call from another script I found which downloads all current versions from a specified library/list. I can’t post my entire solution as the originating script isn’t mine. You can find it here: http://www.sharepointdiary.com/2017/03/sharepoint-online-download-all-files-from-document-library-using-powershell.html#ixzz5YpC0YB00

In the Download-AllFilesFromLibrary function from the above site, I added GetFileVersions as the last statement of the Foreach($File in $FilesColl) loop.

As I said, I’m new to Powershell, so I’m sure some improvements could be made to the function, but it works.

Function GetFileVersions
{
#Get versions of specific document from library
$fileVersions = $file.Versions;
$ctx.Load($fileVersions);
$ctx.ExecuteQuery();
$vcount = $fileversions.count;

If ($vcount -ne “0” ) { 
# Download all versions of specific file as individual docs
$fname,$fext = $file.name.split('.');   # Separate filename and extension to different variables
 foreach ($fversion in $fileVersions) {
    $str = $fversion.OpenBinaryStream(); 
    $ctx.ExecuteQuery(); 
    $dtime = $fversion.created.ToString(“u”) -replace “:”,””;   # Convert date/time created to formatted string and replace semicolons in the time with blank
    $filename =$localfolder + “\” + $fname + “~” + $dtime + “.” + $fext ; # Compose the filename as the target folder, file name + ~+ datetime created.fileextension (i.e. book~2019-02-12 153659Z.xlsx)
    $fs = New-Object IO.FileStream $filename ,'Create','Write','Read'; 
    $str.Value.CopyTo($fs); 

    #To use the Write-Log function below, see the script at https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0

   #$vmessage = $filename + “*” + $fversion.versionlabel;  # Message to write to the log file 
  #Write-Log $vmessage;

    $fs.Close();
   }
        if ($fs.isdisposable) {$fs.Dispose();}  
}
}

Upvotes: 0

LZ_MSFT
LZ_MSFT

Reputation: 4228

The code below for your reference.

string _SharePointSiteURL = @"https://lz.sharepoint.com/sites/lz";

var _SharePointSiteUser = "[email protected]";
var password = "Password";

var localPath = @"c:\test\";

var filePath="Shared Documents/ABC/Test.pdf";

var securePassword = new SecureString();

foreach (char c in password)
{
    securePassword.AppendChar(c);
}

using (var clientContext = new ClientContext(_SharePointSiteURL))
{
    var onlineCredentials = new SharePointOnlineCredentials(_SharePointSiteUser, securePassword);
    clientContext.RequestTimeout = 10000000;
    clientContext.Credentials = onlineCredentials;
    Web web = clientContext.Web;
    clientContext.Load(web, website => website.ServerRelativeUrl, website => website.Url);
    clientContext.ExecuteQuery();

    var spFile = clientContext.Web.GetFileByServerRelativeUrl((web.ServerRelativeUrl.EndsWith("/") ? web.ServerRelativeUrl : web.ServerRelativeUrl + "/") + filePath);
    clientContext.Load(spFile);
    FileVersionCollection versions = spFile.Versions;
    clientContext.Load(versions);
    var oldVersions = clientContext.LoadQuery(versions.Where(v => v != null));
    clientContext.ExecuteQuery();
    if (oldVersions != null)
    {
        foreach (Microsoft.SharePoint.Client.FileVersion _version in oldVersions)
        {

            if (!Directory.Exists(localPath))
            {
                Directory.CreateDirectory(localPath);
            }

            using (var wc = new System.Net.WebClient())
            {                       
                wc.Credentials = onlineCredentials;
                wc.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
                wc.Headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MDDC)";
                wc.DownloadFile(web.Url + "/" + _version.Url, localPath+"Test.pdf");
            }   
        }
    }
}

Upvotes: -1

Gautam Sheth
Gautam Sheth

Reputation: 2500

Ensure that you are using the latest version of SharePoint Online CSOM or atleast a version after September 2017.

You can get the latest CSOM version from Nuget and add it to your project.

Once done, you can use the code mentioned below to download the versions of a file. Modify it as per your environment, creds as well as file name:

var siteUrl = "https://XXXX.sharepoint.com";
var userName = "[email protected]";
var password = "password";

using (ClientContext context = new ClientContext(siteUrl))
{
    SecureString securePassword = new SecureString();
    foreach (char c in password.ToCharArray())
    {
        securePassword.AppendChar(c);
    }

    context.AuthenticationMode = ClientAuthenticationMode.Default;
    context.Credentials = new SharePointOnlineCredentials(userName, securePassword);

    var file = context.Web.GetFileByServerRelativeUrl(siteUrl + "/Documents/test.docx");
    var fileVersions = file.Versions; 
    context.Load(file); 
    context.Load(fileVersions);
    context.ExecuteQuery();

    int index = 0;
    foreach (var version in fileVersions)
    {    
       var str = version.OpenBinaryStream();
       context.ExecuteQuery();

       string filename = string.Format("c:\\Downloads\\test-{0}.docx", index);
       using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate))
       {
         str.Value.CopyTo(fileStream);
       }
       index++;     
    }

}

Reference - CSOM September 2017 update

Upvotes: 1

Per MSDN, I guess you cannot download all the versions at one shot. You have to send versionid appended within brackets like versions(<version id>) to get particular item.

http://<site url>/_api/web/getfilebyserverrelativeurl('/<folder name>/<file name>')/versions(<version id>)

Make sure the expected url is set properly if you want minor version.

Upvotes: 0

Related Questions