Working with Folders and Files Using the SharePoint REST Service and Windows PowerShell

Gary Lapointe

by Gary Lapointe on 7/3/2015

Share this:
Print

Article Details

Date Revised:
7/3/2015

Applies to:
files, folders, Invoke-SPORestMethod, PowerShell, SharePoint Online


In the previous article, Working with Lists and List Items Using the SharePoint REST Service and Windows PowerShell, I showed the PowerShell equivalents to the examples in the Working with lists and list items with REST MSDN article. In this article I want to look at the examples in the next MSDN article in that series, Working with folders and files with REST. As with the lists and list items article, I will use the previously defined Invoke-SPORestMethod function to execute the various REST calls.

Working with folders by using REST

As you may have seen in the previous article, performing GET operations using REST is quite easy. This first example retrieves a specific folder using the GetFolderByServerRelativeUrl REST method. In this case, the folder corresponds to the root folder of the Shared Documents library of the site.

url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')

method: GET

headers:

Authorization: "Bearer " + accessToken

accept: "application/json;odata=verbose" or "application/atom+xml"

Just as in the previous lists and list items article, you can ignore the details about the Authorization header value. In the figure below, I’m loading the functions in the Invoke-SPORestMethod.ps1 file into memory, setting the credentials using the Set-SPOCredentials function, setting the REST URL to a variable, and then I call the Invoke-SPORestMethod function to retrieve the folder details which I then output to the screen:

Figure 1: Use Invoke-SPORestMethod to retrieve a folder using the server relative URL

For subsequent examples I’m not going to show the loading of the Invoke-SPORestMethod.ps1 file or the call to Set-SPORestCredentials as I’m going to assume that you’re using the same PowerShell session and therefore there’s no need to reload the file or provide the credentials again. Also, for the sake of keeping this article a little shorter, I won’t show the output as I’ve shown with Figure 1 and instead will just show the command you need to run.

Creating a folder using the REST service requires you to construct a JSON object that contains all the properties necessary to create the folder. Additionally, you must also provider a form digest which, as you’ll see in the PowerShell equivalent example, you can use the Invoke-SPORestMethod function to retrieve.

url: http://site url/_api/web/folders

method: POST

body: { '__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': '/document library relative url/folder name'}

Headers:

Authorization: "Bearer " + accessToken

X-RequestDigest: form digest value

accept: "application/json;odata=verbose"

content-type: "application/json;odata=verbose"

content-length:length of post body

For the PowerShell equivalent example I’m going to replace the placeholder values from the MSDN example with real values. So in the following example I’m creating a new folder called “My Folder” in the root of the Shared Documents library – note that, as I demonstrated with the lists and list items examples, you can construct the JSON object by either using a static string or by creating a PSCustomObject, which you can then convert to a JSON string. For the rest of the examples in this article, I just use a static string, but for this example, I’ve included both options.

$url = "https://contoso.sharepoint.com/_api/web/folders"
 
# Option 1: Set metadata using PSCustomObject
$body = New-Object PSCustomObject -Property @{"__metadata" = (New-Object PSCustomObject -Property @{"type" = "SP.Folder"}); "ServerRelativeUrl" = "/Shared Documents/My Folder"}
$metadata = ConvertTo-Json $body -Compress
 
# Option 2: Set metadata using a manually constructed string
$metadata = "{ '__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': '/Shared Documents/My Folder'}"
 
# Get the form digest
$digest = (Invoke-SPORestMethod -Url "https://contoso.sharepoint.com/_api/contextinfo" -Method "POST").GetContextWebInformation.FormDigestValue
 
# Create the folder
$folder = Invoke-SPORestMethod -Url $url -Method "POST" -Metadata $metadata -RequestDigest $digest

For the sake of clarity in the examples that follow, I’m going to omit the call to retrieve the form digest and just assume that you have it in a variable for use.

To update a folder you use an URL endpoint that points to the folder you wish to update and then include another JSON structure specifying the properties that you wish to update. Additionally, you must specify the MERGE X-HTTP-Method header value and the IF-MATCH header value along with a form digest.

url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')

method: POST

body: { '__metadata': { 'type': 'SP.Folder' }, 'Name': 'New name' }

Headers:

Authorization: "Bearer " + accessToken

X-RequestDigest: form digest value

"IF-MATCH": etag or "*"

"X-HTTP-Method":"MERGE",

accept: "application/json;odata=verbose"

content-type: "application/json;odata=verbose"

content-length:length of post body

In the following PowerShell example, I’m updating the previously created folder by changing the name to “My Updated Folder”. Note that I use a value of “*” for the -ETag parameter (which corresponds to the IF-MATCH header value) because I don’t care if another user has modified the folder and I just want to overwrite with my changes regardless of what another user may have done.

$url = "https://contoso.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('/Shared Documents/My Folder')"
$metadata = "{ '__metadata': { 'type': 'SP.Folder' }, 'Name': 'My Updated Folder' }"
Invoke-SPORestMethod -Url $url -Method "POST" -XHTTPMethod "MERGE" -Metadata $metadata -RequestDigest $digest -ETag "*"

 If you run this code you might find that your folder name did not, in fact, get updated. At the time of writing this article, this is expected as it is a known bug (or at least, I believe it’s a bug). I wanted to show the example in case they do eventually fix it. So if this doesn’t work then how do you actually update the folder name? Well, the trick is to actually update the list item associated with the folder.

$url = "https://contoso.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('/Shared Documents/My Folder')/ListItemAllFields"
$item = Invoke-SPORestMethod -Url $url
$itemType = $item.__metadata.type
 
$metadata = "{ '__metadata': { 'type': '$itemType' }, 'FileLeafRef': 'My Updated Folder' }"
Invoke-SPORestMethod -Url $url -Method "POST" -XHTTPMethod "MERGE" -Metadata $metadata -RequestDigest $digest -ETag "*"

Note that I’ve added the ListItemAllFields endpoint to the URL and I perform a GET so that I can retrieve the item type for the list. I then use that item type when constructing the metadata variable (again, in this example I’m updating the list item associated with the folder so I can use SP.Folder as the item type; I have to use the actual type value associated with the list item). And finally, instead of setting the Name property, I’m setting the FileLeafRef field, which is where the folder name is actually stored. (Note that this approach will not work with folders not associated with a list and will not work with the root folder of a list.)

To delete the folder created in the previous examples you simply need to provide the endpoint to the folder and then use the DELETE X-HTTP-Method header value and an appropriate IF-MATCH header value. Because this is a pretty simple operation, I’m going to omit the MSDN sample and just jump right to the PowerShell equivalent:

$url = "https://contoso.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('/Shared Documents/My Updated Folder')"
Invoke-SPORestMethod -Url $url -Method "POST" -XHTTPMethod "DELETE" -RequestDigest $digest -ETag "*"

Working with files by using REST

To retrieve the list of files within a specific folder you can use the GetFolderByServerRelativeUrl method in conjunction with the Files endpoint.

url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')/Files

method: GET

headers:

Authorization: "Bearer " + accessToken

accept: "application/json;odata=verbose"

This is a simple example that ultimately is very similar to retrieving the list items from a list; the difference is that this only retrieves the files in the folder specified.

$url = "https://contoso.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')/Files"
$files = Invoke-SPORestMethod -Url $url

Again, this will return back just the metadata about the file and not the file itself. To get the file itself, you have to pass in the specific file name along with the $value expansion value.

url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')/Files('file name')/$value

method: GET

headers:

Authorization: "Bearer " + accessToken

For the PowerShell equivalent example, notice that you have to escape the dollar sign ($) with a tick mark so that the PowerShell runtime doesn’t treat it as an embedded variable:

$url = "https://contoso.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')/Files('photo.jpg')/`$value"
$file = Invoke-SPORestMethod -Url $url
[System.IO.File]::WriteAllBytes("c:\temp\photo.jpg", $file)

After retrieving the file I use the WriteAllBytes static method of the System.IO.File class to save out the file to disk. Alternatively, you could use the -OutFile parameter of the Invoke-SPORestMethod to save the file directly.

Note: If you have an earlier version of the Invoke-SPORestMethod function, I had a small bug where the method was returning a string rather than a byte array so you should get the latest version from the gist repository.

If you know the full server relative URL to the file you can use the GetFileByServerRelativeUrl method instead of using the GetFolderByServerRelativeUrl method:

$url = "https://contoso.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/Shared Documents/photo.jpg')/`$value"
$file = Invoke-SPORestMethod -Url $url
[System.IO.File]::WriteAllBytes("c:\temp\photo.jpg", $file)

To create a new file you again use the GetFolderByServerRelativeUrl method with the Files endpoint and finally use the add method with a couple of parameters to indicate the file name and whether to overwrite the file.

url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')/Files/add(url='a.txt',overwrite=true)

method: POST

body: "Contents of file"

Headers:

Authorization: "Bearer " + accessToken

X-RequestDigest: form digest value

content-length:length of post body

For the PowerShell equivalent you must make sure that you set the request digest (which is retrieved in an earlier example) and then set the -Metadata parameter to the file’s byte array, which I retrieve using the ReadAllBytes static method of the System.IO.File class.

$url = "https://contoso.sharepoint.com/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')/Files/add(url='photo.jpg', overwrite=true)"
$body = [System.IO.File]::ReadAllBytes("c:\temp\photo.jpg")
$file = Invoke-SPORestMethod -Url $url -Method Post -Metadata $body -RequestDigest $digest

You can use this to update an existing file by making sure that the overwrite value is set to true. You can also update a file by setting the X-HTTP-Method header value to PUT and then using the file’s direct endpoint:

$url = "https://contoso.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/Shared Documents/photo.jpg')/`$value"
$body = [System.IO.File]::ReadAllBytes("c:\temp\photo.jpg")
$file = Invoke-SPORestMethod -Url $url -Method Post -XHTTPMethod Put -Metadata $body -RequestDigest $digest

Keep in mind that both of these approaches only update the file itself. To update the file’s metadata you would use an endpoint that points to the file’s list item and update it just as was demonstrated in the previous article. Also, using this approach you can upload files up to 2GB in size. In the MSDN article there is a separate section that discusses uploading large files – this section is applicable when using REST with JavaScript and the cross-domain library and doesn’t apply to using REST with PowerShell.

To check out a file so that you can prevent others from working with the file while you are editing it or if check outs are required you use the CheckOut() method:

url: http://site url/_api/web/GetFileByServerRelativeUrl('/Folder Name/file name')/CheckOut(),

method: POST

headers:

Authorization: "Bearer " + accessToken

X-RequestDigest: form digest value

For the PowerShell equivalent I’m discarding the output of the Invoke-SPORestMethod call by passing it to the $null output stream because the output isn’t of any use:

$url = "https://contoso.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/Shared Documents/photo.jpg')/CheckOut()"
Invoke-SPORestMethod -Url $url -Method Post -RequestDigest $digest > $null

To check the file back in, use the CheckIn() method and pass in values for the check in comment and check in type:

$url = "https://contoso.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/Shared Documents/photo.jpg')/CheckIn(comment='Comment', checkintype=0)"
Invoke-SPORestMethod -Url $url -Method Post -RequestDigest $digest > $null

The MSDN article shows the use of the checkintype property, which I’ve demonstrated above in the PowerShell example but the article doesn’t mention what the values are. The values actually correspond to the Microsoft.SharePoint.SPCheckinType enumeration. So a value of 0 (zero) corresponds to MinorCheckIn; a value of 1 (one) corresponds to MajorCheckIn; and a value of 2 (two) corresponds to OverwriteCheckIn.

Finally, to delete the file simply provide the endpoint to the file and specify DELETE for the X-HTTP-Method header value (I’ve omitted the MSDN example for brevity):

$url = "https://contoso.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/Shared Documents/photo.jpg')"
Invoke-SPORestMethod -Url $url -Method Post -XHTTPMethod Delete -RequestDigest $digest

The MSDN example includes the IF-MATCH header for the etag value but this isn’t necessary unless you want to verify that nobody has changed the file since you last retrieved it.

Working with files attached to list items by using REST

Working with list item attachments is very similar to working with files. The only real difference is the endpoint that you utilize – instead of a accessing the file via the folder or file endpoints shown earlier in this article, you instead access it through the AttachmentFiles endpoint relative to the list item.

The following example demonstrates how to retrieve the list of attachments associated with an item:

url: http://site url/_api/web/lists/getbytitle('list title')/items(item id)/AttachmentFiles/

method: GET

headers:

Authorization: "Bearer " + accessToken

accept: "application/json;odata=verbose"

For the PowerShell equivalent, I’m retrieving the attachments for an item with an ID of 1 from the list “My List” and saving the results to a variable:

$url = "https://contoso.sharepoint.com/_api/web/lists/getbytitle('My List')/items(1)/AttachmentFiles/"
$files = Invoke-SPORestMethod -Url $url

The $files variable will have a results property, which is an array containing the FileName and ServerRelativeUrl of each attachment. There’s also a __metadata property, which has additional details such as the direct URI to the attachment.

As with previous examples this simply returns the metadata for the attachment and not the attachment itself. Use the $value expander to retrieve the actual file (I’ll omit the MSDN example for the remainder of this article for the sake of brevity):

$url = "https://contoso.sharepoint.com/_api/web/lists/getbytitle('My List')/items(1)/AttachmentFiles('photo1.jpg')/`$value"
Invoke-SPORestMethod -Url $url -OutFile "c:\temp\photo1.jpg"

Again, notice that you have to escape the dollar sign.

To add an attachment to an existing item you simply use the add method of the AttachmentFiles endpoint:

$url = "https://contoso.sharepoint.com/_api/web/lists/getbytitle('My List')/items(1)/AttachmentFiles/add(FileName='photo1.jpg')"
$body = [System.IO.File]::ReadAllBytes("c:\temp\photo1.jpg")
$file = Invoke-SPORestMethod -Url $url -Metadata $body -Method Post -RequestDigest $digest

Unlike with adding files to a document library, there is no option to simply overwrite existing files when using the add method for attachments. So to update an existing attachment you must use the PUT method for the X-HTTP-Method header:

$url = "https://contoso.sharepoint.com/_api/web/lists/getbytitle('My List')/items(1)/AttachmentFiles('photo1.jpg')/`$value"
$body = [System.IO.File]::ReadAllBytes("c:\temp\photo1.jpg")
$file = Invoke-SPORestMethod -Url $url -Metadata $body -Method Post -XHTTPMethod Put -RequestDigest $digest

Summary

Using the REST service to manipulate files within SharePoint is very similar to working with list items so the translation process from the MSDN samples to PowerShell should be very familiar and straightforward. Hopefully, between this article and the previous articles, you have seen enough samples to see that by using the Invoke-SPORestMethod helper function it is extremely easy to work with all of the various SharePoint REST endpoints.


Topic: PowerShell

Sign in with

Or register

Connect with Experts