Table of Contents

Working with files

Working with files (documents) is a core aspect of working with SharePoint. Learn how to add/upload files, set metadata and download files again and much more.

In the remainder of this article you'll see a lot of context use: in this case this is a PnPContext which was obtained via the PnPContextFactory as explained in the overview article and show below:

using (var context = await pnpContextFactory.CreateAsync("SiteToWorkWith"))
{
    // See next chapter on how to use the PnPContext for working with files
}

Getting files

In PnP Core SDK files are represented via an IFile interface. Before you get perform a file operation (e.g. like publish or download) you need to get the file as IFile. There are a number of ways to get an IFile like loading a single file via a lookup or enumerating the files in library / folder.

Getting a single file

If you know the name and location of a file you can get a reference to it via the IWeb method named GetFileByServerRelativeUrl. This method takes a server relative path of the file and optionally allows you to specify which properties to load on the file.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Get a reference to the file, loading extra properties of the IFile 
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, f => f.CheckOutType, f => f.CheckedOutByUser);

A file can also be loaded using it's unique id using one of the GetFileById methods:

Guid fileId = Guid.Parse("{D61B52DE-2ED1-41E2-B3FC-360E7286B0F9}");

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByIdAsync(fileId);

// Get a reference to the file, loading extra properties of the IFile 
IFile testDocument = await context.Web.GetFileByIdAsync(fileId, f => f.CheckOutType, f => f.CheckedOutByUser);
Important

See the Updating/reading file metadata chapter below in case you want to update/read the metadata of retrieved file.

Whenever you've a fully qualified link to a file or a sharing link for a file you can use the GetFileByLink methods to get the corresponding IFile object. Note that when the file happens to live in another site or web then under the covers a new PnPContext is instantiated and returned with that file.

// Test regular link
IFile file2 = await context.Web.GetFileByLinkAsync($"https://contoso.sharepoint.com/sites/asite/SiteAssets/somefile.docx");

// Test sharing link
IFile file1 = await context.Web.GetFileByLinkAsync("https://contoso.sharepoint.com/:v:/s/asite/Ed2UMAoNf0tJhAjSJLt94wYBUd9U-ZhCKOoOXZcGS2dLBQ?e=KobeE5");

// Test sharing link with extra properties of the IFile
IFile file1 = await context.Web.GetFileByLinkAsync("https://contoso.sharepoint.com/:v:/s/asite/Ed2UMAoNf0tJhAjSJLt94wYBUd9U-ZhCKOoOXZcGS2dLBQ?e=KobeE5", f => f.CheckOutType, f => f.CheckedOutByUser);
Important

See the Updating/reading file metadata chapter below in case you want to update/read the metadata of retrieved file.

Updating/reading file metadata

If you plan to update the corresponding file metadata when you've used either the GetFileByServerRelativeUrl, GetFileById or GetFileByLink methods then you need to ensure the needed parent list information is loaded as part of this request. Below sample shows the minimal properties to load:

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl,
                     f => f.ListItemAllFields.QueryProperties(li=>li.All, 
                        li => li.ParentList.QueryProperties(p => p.Title, 
                            p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind, 
                                                          p => p.TypeAsString, p => p.Title))));

testDocument.ListItemAllFields["Title"] = "Hello!";
await testDocument.ListItemAllFields.UpdateOverwriteVersionAsync();
// Or
// await testDocument.ListItemAllFields.UpdateAsync()

If you don't load the ParentList details then field data of complex fields (e.g. Url fields) is not loaded correctly and metadata updates might fail.

Note

Files can have audiences set enabling page web parts to conditionally show the file depending on who loads the page. See the Using audience targeting page to learn more.

Getting the file of a list item

A document in a document library is an IListItem holding the file metadata with an IFile holding the actual file. If you have an IListItem you can load the connected file via File property:

var myList = await context.Web.Lists.GetByTitleAsync("My List");

// Load list item with id 1 with it's file
var first = await myList.Items.GetByIdAsync(1, li => li.All, li => li.File);

// Use the loaded IFile, e.g. for downloading it
byte[] downloadedContentBytes = await first.File.GetContentBytesAsync();

Enumerating files

Files do live in an IFolder, document libraries do have a RootFolder property allowing you to enumerate files, but also the IWeb has a collection of Folders, a RootFolder and GetFolderByIdAsync and GetFolderByServerRelativeUrlAsync methods. Once you've a folder you can enumerate the files inside it.

// Get root folder of a library
IFolder folder = await context.Web.Folders.GetFirstOrDefaultAsync(f => f.Name == "SiteAssets");

// Get root folder of the web (for files living outside of a document library)
IFolder folder = (await context.Web.GetAsync(p => p.RootFolder)).RootFolder;

// Get folder collection of a web and pick the SiteAssets folder
await context.Web.LoadAsync(p => p.Folders);
var folder = context.Web.Folders.AsRequested().FirstOrDefault(p=>p.Name == "SiteAssets");

// Load files property of the folder
await folder.LoadAsync(p => p.Files);

foreach(var file in folder.Files.AsRequested())
{
    // Do something with the file
}

Finding files

If you do not know the exact name and location of on or more file and need to find on any part of the filename, you can also perform a FindFiles operation. This operation can be perfomed on an IFolder or an IList

The FindFiles method accepts a string value which is matched to any part of the filename using a case insensitive regular expression and returns all found matches.

Note
  • This operation can be slow, as it iterates over all the files in the list. If performance is key, then try using a search based solution.
  • It's important to use the * to indicate any wild cards in your search string

Find files in a list:

// Get a reference to a list
IList documentsList = await context.Web.Lists.GetByTitleAsync("Documents");

// Get files from the list whose name contains "foo"
List<IFile> foundFiles = await documentsList.FindFilesAsync("*foo*");

Find files in a folder:

// Get a reference to a folder
IFolder documentsFolder = await context.Web.Folders.Where(f => f.Name == "Documents").FirstOrDefaultAsync();

// Get files from folder whose name contains "bar"
List<IFile> foundFiles = await documentsFolder.FindFilesAsync("*bar*");

Getting file properties

A file in SharePoint has properties which can be requested by loading them on the IFile. Below snippet shows some ways on how to load file properties.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Sample 1: Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Sample 2: Get a reference to the file, loading the file Author and ModifiedBy properties
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, w => w.Author, w => w.ModifiedBy);

// Sample 3: Get files by loading it's folder and the containing files with their selected properties
var folder = await context.Web.GetFolderByServerRelativeUrlAsync($"{context.Uri.PathAndQuery}/SiteAssets", 
                    p => p.Name, p => p.Files.QueryProperties(p => p.Name, p => p.Author, p => p.ModifiedBy));
foreach(var file in folder.Files.AsRequested())
{
    // Do something with the file, properties Name, Author and ModifiedBy are loaded
}

File property bag

Each file also has a so called property bag, a list key/value pairs providing more information about the file. You can read this property bag, provided via the IFile Properties property, and add new key/value pairs to it.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, load the file property bag
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, f => f.Properties);

// Enumerate the file property bag
foreach(var property in testDocument.Properties)
{
    // Do something with the property
}

// Add a new property
testDocument["myPropertyKey"] = "Some value";
await testDocument.Properties.UpdateAsync();

Renaming files

To rename a file you can either use the Rename methods or use the MoveTo methods.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, load the file property bag
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Option A: Use the Rename methods
await testDocument.RenameAsync("renamed document.docx");

// Option B: Move the file to rename it
await testDocument.MoveToAsync($"{context.Uri.PathAndQuery}/Shared Documents/renamed document.docx");

Publishing and un-publishing files

Publishing a file will move the file from draft into published status and increase it's major version by one. Publishing can be done using the PublishAsync method, un-publishing a file will bring the file back to draft status and can be done using the UnPublishAsync method.

Note

Publishing a file requires the library to be configured to support major versions. See the EnableVersioning property on the IList interface.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, load the file property bag
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Publish the file
await testDocument.PublishAsync("Optional publish message");

// Un-publish the file
await testDocument.UnpublishAsync("Optional un-publish message");

Checking out, undoing check out and checking in files

In SharePoint a file can be checked out by a user to "lock" the file and then later on checked in again. The same can be done using code, including undoing a checked out of another user via the CheckoutAsync, CheckinAsync and UndoCheckout methods.

Note

Publishing a file requires the library to be configured to support major versions. See the ForceCheckout property on the IList interface.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Check out the file
await testDocument.CheckoutAsync();

// Check in the file
await testDocument.CheckinAsync();

Undoing a checkout:

// Get the default document library root folder
string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, load the check out information as that can be needed before undoing the check out
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, f => f.CheckOutType, f => f.CheckedOutByUser);

// Checkout the file
await testDocument.UndoCheckoutAsync();

Deleting and recycling files

You can delete a file (permanent operation) or move it to the site's recycle bin (the file can be restored). Deleting a file is done using the typical Delete methods like DeleteAsync, recycling is done via RecycleAsync.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Recycle the file
await testDocument.RecycleAsync();

// Delete the file
await testDocument.DeleteAsync();

Sharing files

A file can be shared with your organization, with specific users or with everyone (anonymous), obviously all depending on how the sharing configuration of your tenant and site collection. Check out the PnP Core SDK Sharing APIs to learn more on how you can share a file.

Adding files (=uploading)

Adding a file comes down to create a file reference and uploading the file's bytes and this can be done via the AddAsync method on a Files collection. This method takes a stream of bytes as input for the file contents.

Note
  • See the working with large files page for some more complete file upload/download samples.
  • Don't forget to load the ListItemFields property if you want to set the file properties after adding. This can be done in multiple ways await addedFile.ListItemAllFields.LoadAsync(), await addedFile.LoadAsync(p => p.ListItemAllFields) or addedFile = await context.Web.GetFileByServerRelativeUrlAsync(addedFile.ServerRelativeUrl, p => p.ListItemAllFields).
// Get a reference to a folder
IFolder siteAssetsFolder = await context.Web.Folders.Where(f => f.Name == "SiteAssets").FirstOrDefaultAsync();

// Upload a file by adding it to the folder's files collection
IFile addedFile = await siteAssetsFolder.Files.AddAsync("test.docx", 
                  System.IO.File.OpenRead($".{Path.DirectorySeparatorChar}TestFilesFolder{Path.DirectorySeparatorChar}test.docx"));

Updating file metadata

The library in which you've uploaded a file might have additional columns to store metadata about the file. To update this metadata you first need to load the IListItem linked to the added file via the ListItemAllFields property, followed by setting the metadata and updating the IListItem.

Note

See the working with list items page for information on how to update an IListItem.

// Get a reference to a folder
IFolder documentsFolder = await context.Web.Folders.Where(f => f.Name == "Documents").FirstOrDefaultAsync();

// Upload a file by adding it to the folder's files collection
IFile addedFile = await documentsFolder.Files.AddAsync("test.docx", 
                  System.IO.File.OpenRead($".{Path.DirectorySeparatorChar}TestFilesFolder{Path.DirectorySeparatorChar}test.docx"));
// Load the corresponding ListItem
await addedFile.ListItemAllFields.LoadAsync();
// Set the metadata
addedFile.ListItemAllFields["Field1"] = "Hi there";
addedFile.ListItemAllFields["Field2"] = true;
// Persist the ListItem changes
await addedFile.ListItemAllFields.UpdateAsync();
Note

Files can have audiences set enabling page web parts to conditionally show the file depending on who loads the page. See the Using audience targeting page to learn more.

Updating the file Author, Editor, Created or Modified properties

Each file has an Author property (the one who created the file), an Editor property (the one who last changed the file), a Created property (when was the file added) and a Modified property (when was the file changed). These are system properties and they cannot be simply overwritten. Using the UpdateOverwriteVersion methods this however is possible as shown in below code snippet:

// Load the default documents folder of the site
var doc = await context.Web.Lists.GetByTitleAsync("Documents", p => p.Fields);
// Upload a file
var file = await doc.RootFolder.Files.AddAsync("demo.docx", System.IO.File.OpenRead($".{System.IO.Path.DirectorySeparatorChar}demo.docx"), true);

// Load the file metadata again to get the ListItemFields populated
await file.LoadAsync(p => p.ListItemAllFields)

// Get a user to use as author/editor
var currentUser = await context.Web.GetCurrentUserAsync();

// The new Created/Modified date to set
var newDate = new DateTime(2020, 10, 20);

// Get the earlier loaded Author and Editor fields
var author = doc.Fields.AsRequested().FirstOrDefault(p => p.InternalName == "Author");
var editor = doc.Fields.AsRequested().FirstOrDefault(p => p.InternalName == "Editor");

// Update file properties
file.ListItemAllFields["Title"] = "new title";
file.ListItemAllFields["Created"] = newDate;
file.ListItemAllFields["Modified"] = newDate;
file.ListItemAllFields["Author"] = author.NewFieldUserValue(currentUser);
file.ListItemAllFields["Editor"] = editor.NewFieldUserValue(currentUser);

// Persist the updated properties
await file.ListItemAllFields.UpdateOverwriteVersionAsync();

Downloading files

If you want to download a file you do need to use either the GetContentAsync method if you prefer a Stream as result type or GetContentBytesAsync if you prefer a byte array.

Note

See the working with large files page for some more complete file upload/download samples.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Download the file as stream
Stream downloadedContentStream = await testDocument.GetContentAsync();

// Download the file as an array of bytes
byte[] downloadedContentBytes = await testDocument.GetContentBytesAsync();

Copying and moving files

A file can be copied or moved into another SharePoint location and this can be done using the CopyToAsync and MoveToAsync methods.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl);

// Copy the file, overwrite if existing on the destination
await testDocument.CopyToAsync($"{context.Uri.PathAndQuery}/MyDocuments/document.docx", true);

// Move the file, overwrite if needed
await testDocument.MoveToAsync($"{context.Uri.PathAndQuery}/MyDocuments/document.docx", MoveOperations.Overwrite);

// Move the file with options
await testDocument.MoveToAsync($"{context.Uri.PathAndQuery}/MyDocuments/document.docx", MoveOperations.None, 
                            new MoveCopyOptions 
                            { 
                                KeepBoth = true, 
                                RetainEditorAndModifiedOnMove = true 
                            });
Note

You can also opt for an asynchronous bulk file/folder copy/move via the CreateCopyJobs methods on ISite. See here for more details.

Converting files

Some files can be transformed into another format by calling one of the ConvertTo methods.

Note

Loading the VroomDriveID and VroomItemID when you load the file to convert optimizes performance, these properties will be fetched if not present.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, p => p.VroomItemID, p => p.VroomDriveID);

// Convert the Word document to PDF, this returns a stream
var pdfContent = await testDocument.ConvertToAsync(new ConvertToOptions { Format = ConvertToFormat.Pdf });

// Get a reference to a folder to upload the PDF
IFolder siteAssetsFolder = await context.Web.Folders.Where(f => f.Name == "SiteAssets").FirstOrDefaultAsync();

// Upload content
await siteAssetsFolder.Files.AddAsync("document.pdf", pdfContent, true);

The following values are valid transformation targets and their supported source extensions:

Target Description Supported source extensions
glb Converts the item into GLB format cool, fbx, obj, ply, stl, 3mf
html Converts the item into HTML format eml, md, msg
jpg Converts the item into JPG format 3g2, 3gp, 3gp2, 3gpp, 3mf, ai, arw, asf, avi, bas, bash, bat, bmp, c, cbl, cmd, cool, cpp, cr2, crw, cs, css, csv, cur, dcm, dcm30, dic, dicm, dicom, dng, doc, docx, dwg, eml, epi, eps, epsf, epsi, epub, erf, fbx, fppx, gif, glb, h, hcp, heic, heif, htm, html, ico, icon, java, jfif, jpeg, jpg, js, json, key, log, m2ts, m4a, m4v, markdown, md, mef, mov, movie, mp3, mp4, mp4v, mrw, msg, mts, nef, nrw, numbers, obj, odp, odt, ogg, orf, pages, pano, pdf, pef, php, pict, pl, ply, png, pot, potm, potx, pps, ppsx, ppsxm, ppt, pptm, pptx, ps, ps1, psb, psd, py, raw, rb, rtf, rw1, rw2, sh, sketch, sql, sr2, stl, tif, tiff, ts, txt, vb, webm, wma, wmv, xaml, xbm, xcf, xd, xml, xpm, yaml, yml
pdf Converts the item into PDF format doc, docx, epub, eml, htm, html, md, msg, odp, ods, odt, pps, ppsx, ppt, pptx, rtf, tif, tiff, xls, xlsm, xlsx

Getting file versions

When versioning on a file is enabled a file can have multiple versions and PnP Core SDK can be used to work with the older file versions. Each file version is represented via an IFileVersion in an IFileVersionCollection. Loading file versions can be done by requesting the Versions property of the file. Once you've an IFileVersion you can also download that specific version of the file by using one of the GetContent methods as shown in the example.

Note

For a file to have versions the library needs to be configured to support major versions and/or minor versions. See the EnableVersioning and EnableMinorVersions properties on the IList interface.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, also request the Versions property
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, f => f.Versions);

foreach(var fileVersion in testDocument.Versions)
{
    // Download the file version content as stream
    Stream fileVersionContent = await fileVersion.GetContentAsync();
}

Getting thumbnails for a file

If you need to present a SharePoint file using a thumbnail and some basic file information (like Title) you can use one of the GetThumbnail methods. These methods optionally use an ThumbnailOptions class allowing you to specify one or more standard or custom thumbnail sizes. Once the call has ended you get a collection of IThumbnail instances each having a thumbnail URL completed with the thumbnail size.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, also request the Graph identifiers (VroomItemId and VroomDriveId) to 
// avoid an extra server roundtrip in the GetThumbnailsAsync call
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, f => f.VroomItemID, f => f.VroomDriveID);

// Define the thumbnails to generate
ThumbnailOptions options = new()
{
    // Standard sized thumbnails
    StandardSizes = new List<ThumbnailSize>
    {
        ThumbnailSize.Medium,
        ThumbnailSize.Large
    },

    // Custom sized thumbnails
    CustomSizes = new List<CustomThumbnailOptions>
    {
        new CustomThumbnailOptions
        {
            Width = 200,
            Height = 300,                                
        },
        new CustomThumbnailOptions
        {
            Width = 400,
            Height = 500,
            Cropped = true,
        },
    }
};

var thumbnails = await testDocument.GetThumbnailsAsync(options);

foreach(var thumbnail in thumbnails)
{
    // use thumbnail.Url 
}
Note

Thumbnails can only be retrieved for files living in a document library. For pages in a pages library this will not work.

Getting analytics

Using one of the GetAnalytics methods on IFile gives you back the file analytics for all time, the last seven days or for a custom interval of your choice. The returned List<IActivityStat> contains one row for the all time and seven days statistic requests, if you've requested a custom interval you also choose an aggregation interval (day, week, month) and depending on the interval and aggregation you'll get one or more rows with statistics.

Note

Loading the VroomDriveID and VroomItemID when you load the file to get statistics for optimizes performance, these properties will be fetched if not present.

var file = await context.Web.GetFileByServerRelativeUrlAsync($"{context.Uri.AbsolutePath}/sitepages/home.aspx", p => p.VroomItemID, p => p.VroomDriveID);

// Get analytics for all time
var analytics = await file.GetAnalyticsAsync();

// Get analytics for the last 7 days
var analytics = await file.GetAnalyticsAsync(new AnalyticsOptions { Interval = AnalyticsInterval.LastSevenDays });

// Get analytics for a custom interval for 11 days --> you'll see 11 rows with statistic data, one per day
DateTime startDate = DateTime.Now - new TimeSpan(20, 0, 0, 0);
DateTime endDate = DateTime.Now - new TimeSpan(10, 0, 0, 0);

var analytics = await file.GetAnalyticsAsync(
                new AnalyticsOptions
                {
                    Interval = AnalyticsInterval.Custom,
                    CustomStartDate = startDate,
                    CustomEndDate = endDate,
                    CustomAggregationInterval = AnalyticsAggregationInterval.Day
                });
Note

The value of the CustomStartDate and CustomEndDate parameters must represent a time range of less than 90 days.

Getting an embeddable preview url

Using on of the GetPreview methods allows you to obtain a short-lived embeddable URL for a file in order to render a temporary preview.

Note

Loading the VroomDriveID and VroomItemID when you load the file to get an embeddable URL optimizes performance, these properties will be fetched if not present.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, also request the Versions property
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, p => p.VroomItemID, p => p.VroomDriveID);

// Get Preview URL
var filePreview = await testDocument.GetPreviewAsync(new PreviewOptions { Page = "2" });

// Use outcome, e.g. use filePreview.GetUrl in an IFRAME to show the file preview

Getting file IRM settings

A SharePoint document library can be configured with an Information Rights Management (IRM) policy which then stamps an IRM policy on the documents obtained from that library. Use the InformationRightsManagementSettings property to read the file's IRM settings.

Note

The library holding files you want to protect need to be first setup for IRM by enabling IRM on it via the IrmEnabled property.

string documentUrl = $"{context.Uri.PathAndQuery}/Shared Documents/document.docx";

// Get a reference to the file, also request the Versions property
IFile testDocument = await context.Web.GetFileByServerRelativeUrlAsync(documentUrl, f => f.InformationRightsManagementSettings);

var fileAccessExpirationTimeInDays = testDocument.InformationRightsManagementSettings.DocumentAccessExpireDays;