Upload Files to SharePoint Online Directly from Power Apps (Without Power Automate!)

Learn how to upload files directly from Power Apps to SharePoint Online without the need for SubmitForm, Power Automate, or anything else beyond a Power Fx formula.

Pre-Requisites

To make this fancy bit of code work, you'll first want to:
  1. Turn on the experimental 'ParseJSON function and untyped objects' feature from within your Power Apps settings
  2. Add the Office 365 Groups connector as a data source to your app
  3. "Borrow" something like the Attachments Control from a Form to add your files to

Overview

Using the Office 365 Groups connector, and the (experimental) untyped objects functionality brought to canvas apps from the ParseJSON feature, we're now able to take advantage of a myriad of Graph API calls and - importantly - parse the response received from them directly inside of Power Apps without the use of Power Automate.

The below functionality is just a glimpse into what can be accomplished, but it tackles one of the more common scenarios of uploading a file to SharePoint Online from Power Apps (which just so happens to be one of the first contributions I made to the Power Platform community).

For now, I'll let the code comments do most of the talking - but in essence we're making a series of REST API calls through the Microsoft Graph API to programatically upload a file to SharePoint Online (fancy buzzword quota met).

You'll want to make the necessary configurations in the initial with variables, but can otherwise run it as-is. There's plenty of areas for optimization, but it offers a solid foundation already in a way that's generally re-usable. The output from this formula, aside from creating the file, is a context variable called 'ctxUploadedFileUrl' which captures exactly what it states - the returned url of the successfully uploaded file.

Here's the code:
// made with ♥ by @ohthreesixfive With( // you'll want to update these initial with variables for your scenario { // eg: "https://contoso.sharepoint.com/sites/operations" (Note: Do not include a trailing slash.) wthSharePointSiteUrl: "{Configure-SharePoint-Site-URL-Here}", // eg: "Audit Files" (Note: Use the Display Name of the Document Library) wthDocumentLibraryName: "{Configure-Document-Library-Name-Here}", // eg: "First(AttachmentsControl).Name" || "samplefolder/hello-world.docx" wthFileName: "{Configure-File-Name-Here}", // eg: "First(AttachmentsControl).Value" (Note: The Graph API currently limits us to files of 4MB or less.) wthFileContent: Blank() }, // you can leave the remaining logic alone unless you know what you're doing or don't mind breaking things ☺ With( { // resolve the host name from the provided site url (eg: "https://contoso.sharepoint.com/sites/operations" => "contoso.sharepoint.com") wthHostName: $"{Index( Split( Index( Split( wthSharePointSiteUrl, "https://" ), 2 ).Value, ".com" ), 1 ).Value}.com", // resolve the site's relative path from the provided site url (eg: "https://contoso.sharepoint.com/sites/operations" => "/sites/operations") wthServerRelativePath: $"{Index( Split( wthSharePointSiteUrl, ".com" ), 2 ).Value}" }, With( { // get the site information from the graph api wthRequestBody: Office365Groups.HttpRequest( $"https://graph.microsoft.com/v1.0/sites/{wthHostName}:{wthServerRelativePath}?$select=id", "GET", "" ) }, With( { // resolve the site id from the response's id field (eg: "contoso.sharepoint.com,6fcfe1d9-faf5-451f-8a91-ded0005629ed,57f57702-0535-47a1-b377-0a36ba65f0d8" => "6fcfe1d9-faf5-451f-8a91-ded0005629ed") wthSiteId: Index( Split( Text(wthRequestBody.id), "," ), 2 ).Value }, With( { // get a list of document libraries from the provided site wthRequestBody_drives: Office365Groups.HttpRequest( $"https://graph.microsoft.com/v1.0/sites/{wthSiteId}/drives?$select=id,name", "GET", "" ) }, With( // parse the request response into a two-column table (id, name) while casting each field to text { wthAllDrives: ForAll( Table(wthRequestBody_drives.value), { id: Text(ThisRecord.Value.id), name: Text(ThisRecord.Value.name) } ) }, With( // lookup the id for the document library whose name maches the provided document library name { wthDriveId: LookUp( wthAllDrives, name = wthDocumentLibraryName ).id }, With( // put the new file content into the document libary using the provided name and file content { wthRequestBody_upload: Office365Groups.HttpRequest( $"https://graph.microsoft.com/v1.0/sites/{wthSiteId}/drives/{wthDriveId}/root:/{wthFileName}:/content", "PUT", wthFileContent ) }, UpdateContext({ctxUploadedFileUrl: Text(wthRequestBody_upload.webUrl)}) ) ) ) ) ) ) ) );

Discuss