C# – Download Sharepoint folders recursive

Veröffentlicht von

Download complete Sharepoint folder with subfolders and files with C#.

I recently had the problem, that I needed to download a complete folder from Sharepoint (Microsoft Teams) recusivly to my local drive. Here is my solution. Prequesite is that we already have an authenticated ClientContext, which we will use in the code (_context).

The main function

The main function I am using, first the URL is normalized, you can find the function here. The normalization extracts the part of the URL we can use for accessing the file. Example:

URL: https://site.sharepoint.com/sites/Team/Documents/General/folder/html
Normalized: /sites//Team/General/folder/html

In the next step, we use some regex, to get the end of the path (the folder we want to download), “html” in the example above, the rest is the base url.

Then we can use the URL to read the folders and build the file list. After that we process the file list and build the download list. In the end we download the file list, we gathered.

 // <summary>
/// Download a Sharepoint folder
/// </summary>
/// <param name="url"></param>
/// <param name="targetFolder"></param>
public void DownloadFolder(string url, string targetFolder)
{
	url = NormalizeUrl(url);
	var baseUrl = "";
	var urlFolderName = "";

	{

		var match = new Regex("(?<base>(.*))\\/(?<folder>.+)").Match(url);
		if (match.Success)
		{
			baseUrl = match.Groups["base"].Value.Trim();
			urlFolderName = match.Groups["folder"].Value.Trim();
		}
	}

	var rootFolders = _context.Web.GetFolderByServerRelativeUrl(baseUrl).Folders;
	_context.Load(rootFolders, folders => folders.Include(f => f.ListItemAllFields));
	_context.ExecuteQuery();

	var sharepointList = new List<string>();

	foreach (var folder in rootFolders)
	{
		if (folder.ListItemAllFields.FieldValues.ContainsKey("FileLeafRef"))
		{
			var folderName = (string)folder.ListItemAllFields.FieldValues["FileLeafRef"];
			if (folderName == urlFolderName)
			{
				GetFilesAndFolders(_context, folder, "", sharepointList);
			}
		}
	}

	var downloadItemList = new List<DownloadItem>(); //list for local files

	//create paths for further processing
	foreach (var item in sharepointList)
	{
		var temp = item.Replace("/", "\\");
		temp = temp.Replace(urlFolderName + "\\", "").Trim('\\');

		var localFileName = Path.Combine(targetFolder, temp);
		var remotePath = baseUrl + item;

		var downloadItem = new DownloadItem
		{
			LocalTargetPath = localFileName,
			RemotePath = remotePath
		};


		downloadItemList.Add(downloadItem);
	}

	//download items
	DownloadItems(downloadItemList);
}

Build the file list for download

In order to build the list of files, we use a recursive function, which is reading all files and folders from the location. The list is returned from the function.

 /// <summary>
/// Get all files, folder (recursive) from a sharepoint location
/// </summary>
/// <param name="context"></param>
/// <param name="folder"></param>
/// <param name="folderName"></param>
/// <param name="sharepointList"></param>
private void GetFilesAndFolders(ClientContext context, Folder folder, string folderName, List<string> sharepointList)
{
	var fileList = "";

	if (folder != null && folder.ListItemAllFields.FieldValues.Count > 0)
	{
		folderName += "/" + (string)folder.ListItemAllFields.FieldValues["FileLeafRef"];
		fileList += "Folder:" + folderName + Environment.NewLine;

		var fileCollection = folder.Files;
		context.Load(fileCollection, files => files.Include(f => f.ListItemAllFields));
		context.ExecuteQuery();

		foreach (var file in fileCollection)
		{
			var fileName = folderName + "/" + (string)file.ListItemAllFields.FieldValues["FileLeafRef"];
			fileList += fileName + Environment.NewLine;
			sharepointList.Add(fileName);
		}

		var subFolderCollection = folder.Folders;
		context.Load(subFolderCollection, folders => folders.Include(f => f.ListItemAllFields));
		context.ExecuteQuery();
		foreach (var subFolder in subFolderCollection)
		{
			GetFilesAndFolders(context, subFolder, folderName, sharepointList);
		}
	}
}

Download the file

For downloading the items, we have a list of download items, the download item is very simple:

public class DownloadItem
{
	/// <summary>
	/// the local path where the downloaded file should be put at
	/// </summary>
	public string LocalTargetPath { get; set; } = string.Empty;

	/// <summary>
	/// the remote path where the file should be downloaded from
	/// </summary>
	public string RemotePath { get; set; } = string.Empty;

	public override string ToString()
	{
		return RemotePath + " - " + LocalTargetPath;
	}
}

The download function:

/// <summary>
/// Downloads a file from sharepoint
/// </summary>
/// <param name="link"></param>
/// <param name="targetDocument"></param>
public void DownloadFile(string link, string targetDocument)
{
	var relativeUrl = NormalizeUrl(link);
	var fileInfo = File.OpenBinaryDirect(_context, relativeUrl);

	using (Stream destination = System.IO.File.Create(targetDocument))
	{
		for (var a = fileInfo.Stream.ReadByte(); a != -1; a = fileInfo.Stream.ReadByte())
			destination.WriteByte((byte)a);
	}
}

Usage

Now we have everything prepared, we can use or class for download like that.

var url = "https://site.sharepoint.com/sites/Team/Documents/General/folder/html";
var targetFolder = "c:\\temp\\sharepoint";
SharepointAccess.DownloadFolder(url, targetFolder);

This will download the sharepoint folder to its target location. And well thats it!

Update

In the comments was asked about the “DownloadItems” function, here it is:

/// <summary>
/// Download the list of items
/// </summary>
/// <param name="items"></param>
private void DownloadItems(List<DownloadItem> items)
{
    foreach (var item in items)
    {
        var targetFolder = Path.GetDirectoryName(item.LocalTargetPath);
        if (targetFolder != null && !Directory.Exists(targetFolder))
        {
            Directory.CreateDirectory(targetFolder);
        }

        DownloadFile(item.RemotePath, item.LocalTargetPath);
    }
}

4 Kommentare

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert