This is an overview of how to move files with Microsoft Flow using the
SharePoint - Send an HTTP request to SharePoint action and SharePoint Online REST APIs.
These SharePoint Online REST APIs can be used outside of Flow, but you will need to handle authentication and provide an X-RequestDigest header with any POST requests (as with any SP REST POST request).
In the case of Flow's
SharePoint - Send an HTTP request to SharePoint action, authentication is handled for you and you do not need to provide the X-RequestDigest header.
This is something I came across while building a two-stage approval workflow with Flow, which had the following requirements:
- Allow users to submit a document for approval (using Flow's
SharePoint - For a selected itemtrigger)
- When triggered, the Flow should create a copy of the document in a
Stage 1 Approvalfolder (in the same library) and start an approval process on the approval copy of the document via the
Approvals - Start an approvalaction
- If approved at stage 1, move the approval copy of the document to a
Stage 2 Approvalfolder (in the same library) and start another approval process
- If rejected, move the approval copy of the document to a
- If approved at stage 1, move the approval copy of the document to a
- If approved at stage 2, move the approval copy of the document to an
While Flow already had a
SharePoint - Copy file action, there wasn't (at least when I wrote this) a
SharePoint - Move file action.
At this point I started looking at using the
SharePoint - Send an HTTP request to SharePoint action (the most useful Flow SharePoint action?) with SharePoint Online REST APIs to move files.
Several samples online (including Microsoft documentation) suggest the following API call (or some variation) to move a file:
https://tenant.sharepoint.com/_api/web/getFileByServerRelativeUrl('/Shared Documents/FileName.docx')/moveTo(newurl='/Shared Documents/FolderName/FileName.docx',flags=1)
In the example above,
flags=1 means overwrite an existing file at
newurl if there is one, rather than returning an error.
This was fine in early development, but with more testing I found the following issues:
- Long file names / paths can quickly exceed URL limits causing the API request to fail
'characters in file/folder names are problematic
I spent some time trying to work around these issues, but was asking myself why I was battling with sending POST data in the request URL that should really (in my humble opinion) be included in the POST body instead.
And then I started thinking about how SharePoint Online's modern document library UI has a 'Move To' button...
If you're curious about how SharePoint Online's modern UI uses SharePoint REST APIs, you can inspect the Network traffic sent from the SharePoint page using either the Network tab of your browser developer tools, or a debugging proxy like Telerik's excellent (and free) Fiddler.
This can be a useful technique for discovering as-yet undocumented APIs or how to use APIs that are documented but not explained clearly enough with examples.
In this post I'll focus on using the Network tab of Chrome's developer tools, but the same principals should apply to other browser development tools.
To only show API requests, you'll want to filter traffic shown in the Network tab by clicking on the XHR heading. You can additionally filter by a text value in the 'Filter' text box.
If you navigate to a SharePoint Online document library that's configured to show the modern UI and (while inspecting browser network traffic) use the 'Move To' button to move a file to a different folder in the same library, you'll see the following REST request:
This endpoint accepts source and destination URLs in the request body rather than the request URL, which means it doesn't have the drawbacks of the
getFileByServerRelativeUrl('/from/')/moveTo(newUrl='/to/') approach. 🎉
If you try and move a file to a folder that already contains a file with that name, the API request will return an error (HTTP 400) and you'll see the prompt
A file with this name already exists with an option to
replace the existing one. If you click Replace, the request is sent again with an additional parameter:
If you inspect the request body (in Chrome this is via Headers > Request payload), you'll see something like the following JSON:
"DecodedUrl": "https://tenant.sharepoint.com/Shared Documents/FileName.docx"
"DecodedUrl": "https://tenant.sharepoint.com/Shared Documents/FolderName/FileName.docx"
As you can see, the DecodedUrl values are absolute URLs that include the source and destination file names.
You can recreate this request in Flow using the
Data Operations - Compose and
SharePoint - Send an HTTP request to SharePoint actions, as follows (using the example JSON above as a starting point for the
Data Operations - Compose action's
Creating the request body in a separate
Data Operations - Compose action makes debugging the Flow easier if something goes wrong with the request.
When configuring the
SharePoint - Send an HTTP request to SharePoint action, you should specify the following values:
|The SharePoint site URL
|See Headers table below
Output of the
Data Operations - Compose action
Note: swap out the Uri value for
_api/SP.MoveCopyUtil.MoveFileByPath() if you don't want to overwrite an existing file in the destination.
Well, almost. If you only want to move files from one location in a document library to another location in the same document library, that's pretty much the end of the story.
But what about...
If you click the Copy To button in the modern library UI while inspecting browser network traffic, you'll see the following request (or a variation thereof):
SP.MoveCopyUtil.MoveFileByPath, you can recreate this request in Flow with the
SharePoint - Send an HTTP request to SharePoint action to copy files.
When moving files from one location in a library to another location in the same library, the list item associated with the source and destination file is the same, meaning that version history and field values are automatically preserved, as you are only updating the file location within the library.
If you use the above approach to move a file from one library to another library, or even to another site or site collection, the list item associated with the source and destination file are not the same (think of it as a 'copy' followed by a 'delete' of the original file) and version history is not maintained.
In this case field values will be replicated from source to destination (if possible).
However, although it's possible to move files across site collections with
SP.MoveCopyUtil.MoveFileByPath, if you move a file from one site collection to another via the modern UI's Move To button while looking at your browser's network traffic, you'll notice that it switches to a different set of APIs...
I've experimented a little with recreating the CreateCopyJobs and GetCopyProgress APIs in Flow, but these probably deserve their own post. So far I have noticed the following from inspecting network traffic while moving files from one site collection to another via the Move To button and recreating the calls from Flow:
CreateCopyJobsAPI will return an error if fields from the source list are missing in the destination list, unless an
AllowSchemaMismatchparameter in the request body is specified as
true. This is matched by the UI prompting you of the same (missing fields), with a button to proceed anyway, which then resends the request with the
AllowSchemaMismatchparameter set to
- Version history is copied when using
IgnoreVersionHistoryparameter set to
false. If the parameter is set to
true, the destination file will be version 1.0.
- If the destination file already exists when the
IsMoveModeparameter is set to
true, the copy job will fail (see the Logs when inspecting the output of GetCopyJobProgress). There doesn't appear to be an 'overwrite' parameter in the
CopyMigrationOptionsclass to allow overwrite on a move. This also happens with the UI Move To button - the following error message is displayed:
A file or folder with this name already exists. Please rename the file or folder and try again.
API references for CreateCopyJobs and GetCopyJobsProgress
Hopefully someone finds the above useful 🙂
|22nd June 2018
|Updated to reference 'SharePoint Online' rather than 'SharePoint', as the post content has only been tested in that context
|22nd June 2018
|Updated info on CreateCopyJobs and GetCopyJobProgress based on further testing