Hello ✋,
First, is Box Drive an option? Using our pre-built solution takes cares of this type of use case most often. It will sync all content from the local folder up to the cloud, as well as make sure the local version is up to date for folks on the machine using the file. It doesn’t request parsing through the list, as it does all that for you automatically.
If Box Drive is not an option for some reason, I would recommend using one of our higher level Box SDKs in Python or Node as an example. Direct powershell is great for some less critical automations - like folder/user provisioning, but it is not great for error handling/retries.
Are you using the Box CLI in tandem with Powershell or just Powershell directly? The CLI might help because it will do the direct/chunked upload choice for you.
Do you never get a response? Or only sometimes?
Thanks,
Alex, Box Developer Advocate 🥑
Solution: Use a combination of Powershell and @boxcli.
Box Drive was not an option, as I needed to run it as a service account without
logging into the machine. I set it up as a Scheduled Task.
Action: Start a program
Program: Powershell
Arguments: -File "Drive:\Path\Send-BoxFiles.ps1" -Path Drive:\PathToFiles\ -FolderId 123456789012
I installed @boxci into its default location. The application I’m using is configured
for JWT authentication, so in the Box Developers Panel, I downloaded the app settings
as a json and stored them on the server. Through powershell, I logged in as the service account
that will be running the scheduled task and configured the environment.
runas /user:service_account powershell
cd 'C:\Program Files\@boxcli\bin'
.\box configure:environments:add Drive:\Path\config.json
From there, the only command I could get to output to a variable was the creation
of the token, which was fortunate because I was able to perform all other operations
natively in powershell. A copy of the script I used is below. It’s crude but over time
I’ll add in checks and balances, and more logging. Parameters allow you to use the same
script with different users/environments, and paths.
aCmdletBinding()]
Param (
>Parameter(Mandatory=$true)]
rstring] $Path,
>Parameter(Mandatory=$true)]
rstring] $FolderID
)
# Set the Box CLI executable path
$BoxCLIPath = "C:\Program Files\@boxcli\bin"
if(-not (Get-Module Posh-SYSLOG)) {
Import-Module -Name Posh-SYSLOG
}
# Set your Box API access token
Set-Location -Path $BoxCLIPath
$AccessToken = .\box tokens:get
# Construct the URL to upload the file
$UploadURL = "https://upload.box.com/api/2.0/files/content"
# Construct the authorization header
$Headers = @{
"Authorization" = "Bearer $AccessToken"
}
# Get list of files
$files = Get-ChildItem -Path $Path
# Process each file individually
foreach ($file in $files) {
$FilePath = $file.Fullname
# Read the file content
$FileContent = eSystem.IO.File]::ReadAllBytes($FilePath)
# Construct the boundary for multipart form data
$Boundary = aSystem.Guid]::NewGuid().ToString()
# Construct the multipart form data
$FormData = @"
--$Boundary
Content-Disposition: form-data; name="attributes"
Content-Type: application/json
{"name": "$(Split-Path $FilePath -Leaf)", "parent": {"id": "$FolderID"}}
--$Boundary
Content-Disposition: form-data; name="file"; filename="$(Split-Path $FilePath -Leaf)"
Content-Type: application/octet-stream
$($FileContent -join "`r`n")
--$Boundary--
"@
# Set the content type header
$Headerse"Content-Type"] = "multipart/form-data; boundary=$Boundary"
# Upload the file to Box
try {
$Response = Invoke-RestMethod -Uri $UploadURL -Method Post -Headers $Headers -Body $FormData
Send-SyslogMessage -Server syslog.local -Severity Informational -Facility kern -ApplicationName Send-BoxFiles -Message "File Upload Success $file $$($Response.entries.id)]"
Remove-Item -Path $file.FullName
} catch {
Send-SyslogMessage -Server syslog.local -Severity Informational -Facility kern -ApplicationName Send-BoxFiles -Message "$($_.Exception.Message)"
}
}