0

So I am writing this script that will allow me to upload folders and files to a SharePoint document library. In the moment I have one folder (TEST) on my local computer that contains a few other folders, which contain files. I am able to upload folders and files to SharePoint no problem, but I am having difficulty putting them in the correct File Structure. Below I am creating all the folders I need in the SharePoint site. I call the Graph API, and it creates all the folders at the root, but some of these folders don't belong at the root, but exist inside some of the other folders. I know I need to change the $CreateFolderURL, but I am unsure of how to keep track of which folder or file belongs inside of which folder. Basically I want the same local subdirectory structure replicated in the SharePoint Library

$Files = Get-ChildItem "C:\Users\Mark\Documents\TestUpload" -Recurse
write-host $Files

AllFolders($Files)
   
function AllFolders($Files){
    $CreateFolderURL = "https://graph.microsoft.com/v1.0/drives/(DriveID)/items/root/children"

    foreach($file in $Files){
         
         #check if folder or file
       if (! $file.PSIsContainer)
        {
        write-host "File"
        
        }
        else{
        
            
            $uploadFolderRequestBody = @{
            name= "$file"
            folder = @{}
            "@microsoft.graph.conflictBehavior"= "rename"
            } | ConvertTo-Json
   

    
            Invoke-RestMethod -Headers $header -Method Post -Body $uploadFolderRequestBody -ContentType "application/json" -Uri $CreateFolderURL


        }
        
    }
}
4
  • Perhaps I missed something, but if you do not know which file belongs in which directory, then we don't either. Does something in the file name tell which directory it belongs? Commented Mar 10, 2022 at 13:37
  • Sorry guess I should explain a little more. I have a folder on my local computer that contains a few more folders, that contain files. I want to upload all these to SharePoint. I know the local file path, just trying to figure out how to get all the folders in the same order on SharePoint when I upload them Commented Mar 10, 2022 at 14:04
  • Are you saying that you want the same local subdirectory structure replicated at the upload location? Commented Mar 10, 2022 at 15:15
  • Yes that is Exactly what I want Commented Mar 10, 2022 at 15:17

2 Answers 2

1

This is not tested. The goal of this appears to be to create the subdirectory structure.

If you are on PowerShell 5.1 or higher, Get-ChildItem can specify a -Directory switch obviating the need to check PSIsContainer. The key is to replace the local base directory path with the URL path when setting $CreateFolderURL.

AllFolders('C:\Users\Mark\Documents\TestUpload')
   
function AllFolders($BaseDirectory) {
    $Directories = Get-ChildItem -Path $BaseDirectory -Recurse -Directory

    foreach ($Directory in $Directories) {
        $CreateFolderURL = 'http://graph.microsoft.com/v1.0/drives/(DriveID)/items/root/' +
            $_.FullName.Replace($BaseDirectory,'').Replace('\','/')
        $uploadFolderRequestBody = @{
            name= "$file"
            folder = @{}
            "@microsoft.graph.conflictBehavior"= "rename"
        } | ConvertTo-Json

        Invoke-RestMethod -Headers $header -Method Post -Body $uploadFolderRequestBody `
            -ContentType "application/json" -Uri $CreateFolderURL
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly what I needed. Thanks!
0

Using the example above in conjunction with some help on file uploads from https://powershell.works/2022/01/22/upload-files-2-sharepoint-online-using-graph-api/, I wrote the following script. Please not that I'm using powershell on linux, so the renaming of the backslashes wasn't necessary for me.

    ## Get the Token
    $clientId = "Application (Client) ID"
    $clientSecret = "Client secret"
    $tenantName = "TenantName.onmicrosoft.com"
    
    $tokenBody = @{
    
        Grant_Type    = 'client_credentials'
        Scope         = 'https://graph.microsoft.com/.default'
        Client_Id     = $clientId
        Client_Secret = $clientSecret
    }
    
    $tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $tokenBody -ErrorAction Stop
    
    $headers = @{
    
        "Authorization" = "Bearer $($tokenResponse.access_token)"
        "Content-Type"  = "application/json"
    }
    
    ## Use the SharePoint groups ObjectID. From this we'll get the drive ID.
    $site_objectid = "Groups ObjectID"
    
    ## Create all the folders on the SharePoint site first. I've set microsoft.graph.conflictBehavior below to fail because I never want to rename or replace folders.
    # Set the base directory first. 
    $baseDirectory = "/test"
        $directories = get-childItem -path $baseDirectory -recurse -directory
    
        foreach ($directory in $directories) {
        $URL = "https://graph.microsoft.com/v1.0/groups/$site_objectid/sites/root"
    $subsite_ID = (Invoke-RestMethod -Headers $headers -Uri $URL -Method Get).ID
    
    $URL = "https://graph.microsoft.com/v1.0/sites/$subsite_ID/drives"
    $Drives = Invoke-RestMethod -Headers $headers -Uri $URL -Method Get
    
    $Document_drive_ID = ($Drives.value | Where-Object { $_.name -eq 'Documents' }).id
            $createFolderURL = "https://graph.microsoft.com/v1.0/drives/$Document_drive_ID/items/root:{0}:/children"  -f $directory.parent.FullName
            $file = $directory.Name
            
            $uploadFolderRequestBody = @{
                name= "$file"
                folder = @{}
                "@microsoft.graph.conflictBehavior"= "fail"
            } | ConvertTo-Json
    
            invoke-restMethod -headers $headers -method Post -body $uploadFolderRequestBody -contentType "application/json" -uri $createFolderURL
        }
    
    ## Upload the files. I'm only adding files that are 4 days old or less because I run the script every 3 days for backup. 
    ## These are set in the $sharefiles variable. To upload all files just remove everything after the pipe.
    
    $sharefiles = get-childItem  $baseDirectory -recurse | Where-Object {$_.LastWriteTime -gt (Get-Date).AddDays(-4)}
        foreach ($sharefile in $sharefiles) {
    $Filepath = $sharefile.FullName
    $URL = "https://graph.microsoft.com/v1.0/groups/$site_objectid/sites/root"
    $subsite_ID = (Invoke-RestMethod -Headers $headers -Uri $URL -Method Get).ID
    
    $URL = "https://graph.microsoft.com/v1.0/sites/$subsite_ID/drives"
    $Drives = Invoke-RestMethod -Headers $headers -Uri $URL -Method Get
    
    $Document_drive_ID = ($Drives.value | Where-Object { $_.name -eq 'Documents' }).id
    $Filename = $sharefile.Name
    
    $upload_session = "https://graph.microsoft.com/v1.0/drives/$Document_drive_ID/root:{0}/$($Filename):/createUploadSession" -f $sharefile.directory.FullName
    
    $upload_session_url = (Invoke-RestMethod -Uri $upload_session -Headers $headers -Method Post).uploadUrl
    
    ## We'll upload files in chunks.
    
    $ChunkSize = 62259200
    $file = New-Object System.IO.FileInfo($Filepath)
    $reader = [System.IO.File]::OpenRead($Filepath)
    $buffer = New-Object -TypeName Byte[] -ArgumentList $ChunkSize
    $position = 0
    $counter = 0
    
    Write-Host "ChunkSize: $ChunkSize" -ForegroundColor Cyan
    Write-Host "BufferSize: $($buffer.Length)" -ForegroundColor Cyan
    
    $moreData = $true
    
    
    While ($moreData) {
        #Read a chunk
        $bytesRead = $reader.Read($buffer, 0, $buffer.Length)
        $output = $buffer
        If ($bytesRead -ne $buffer.Length) {
            #no more data to be read
            $moreData = $false
            #shrink the output array to the number of bytes
            $output = New-Object -TypeName Byte[] -ArgumentList $bytesRead
            [Array]::Copy($buffer, $output, $bytesRead)
            Write-Host "no more data" -ForegroundColor Yellow
        }
        #Upload the chunk
        $Header = @{
            'Content-Range'  = "bytes $position-$($position + $output.Length - 1)/$($file.Length)"
        }
    
        Write-Host "Content-Range  = bytes $position-$($position + $output.Length - 1)/$($file.Length)" -ForegroundColor Cyan
        #$position = $position + $output.Length - 1
        $position = $position + $output.Length
        Invoke-RestMethod -Method Put -Uri $upload_session_url -Body $output -Headers $Header -SkipHeaderValidation
        #Increment counter
        $counter++
    }
    
    $reader.Close()
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.