Pode
Pode copied to clipboard
Issue #1096 - Add Save-PodeRequestFile functionality - disable overwrite and return filepaths
Description of the Change
Added functionality to make it possible to disable overwriting files, and instead add a numerical suffix to the filename if it already exists in the directory. Enabled via Switch parameter.
Added functionality to make it possible to return a list of filepaths of the uploaded files. Enabled via Switch parameter.
Help comment updated to include parameters and an example.
Related Issue
Resolves #1096
Examples
Parameter NoOverwrite If supplied, disables overwriting already existing files, and adds a numerical suffix to the filename if necessary. F.x. if C:\pode\test.txt already exists and test.txt is uploaded again, it will be saved as C:\pode\test (1).txt
Parameter Return If supplied, filepaths of all saved files will be returned as a list.
Usage $filePaths = Save-PodeRequestFile -Key 'avatar' -Path 'F:/Images' -NoOverwrite -Return
Result In the above example, if F:/Images already contains test.jpg and you're uploading the files test.jpg and asdf.jpg. test.jpg will be saved as test (1).jpg and asdf.jpg as asdf.jpg, the function will then return a list containing the following filepaths:
F:/Images\test (1).jpg F:/Images\asdf.jpg
Hey @acommonusername,
Thanks for the PR! Looking at this there's quite a few use cases this would have to cover when -NoOverwrite is supplied:
- File names with no extension:
.gitignore - File names with multiple
.:example.2023.05.11.txt - Files being saves to a PS Drive or SMB, in this instance
Test-Pathhas to be used as[Path]::Exists(...)doesn't work - Existing file names with the
(id)suffix that could range from 1 > 1000 - ie: this would result in 1000 exists checks
I have built something similar once before, and it would be worth moving the "file ID suffix builder" into it's own function in Private/Helpers.ps1; checking if -NoOverwrite was supplied back in Save-PodeRequestFile, and then calling the new function.
Using the logic I've built before, and combing your logic produces the following (which should cover all use-cases above):
param(
$FilePath = 'C:\temp\tests.txt'
)
# check the initial file path, and use that if it doesn't exist
if (!(Test-Path $filepath)) {
return $filepath
}
# vars for some regex
$fileRegex = '(.+)(\.(?=[^\\\/\.]+$))'
$idRegex = '^.+? \((?<id>\d+)\).*$'
# split the file path into base path and name
$filepath, $filename = $filepath -split '[\\/](?=[^\\\/]+$)'
# build a wildcard file name pattern for filtering existing files
# the regex check is for ".name" use cases where there is no extension
if ($filename -imatch $fileRegex) {
$wildFilename = $filename -replace $fileRegex, '$1 (*)$2'
} else {
$wildFilename = "$($filename) (*)"
}
# calculate the next "id" based on existing files
$nextId = 1
$files = @(Get-ChildItem -Name -File -Force -Path $filepath -Filter $wildFilename |
Sort-Object -Property { $_.Length },{ $_ })
$lastId = $files[-1] -replace $idRegex, '$1'
if (($file.Length -eq $lastId)) {
$nextId = $lastId + 1
} else {
foreach ($file in $files) {
if (($file -replace $idRegex, '$1') -ne $nextId) {
break
}
$nextId++
}
}
# generate the new file name
if ($filename -imatch $fileRegex) {
$filename = $filename -replace $fileRegex, "`$1 ($($nextId))`$2"
} else {
$filename = "$($filename) ($($nextId))"
}
# combine the new file name with the original base path, and return
$filepath = [System.IO.Path]::Combine($filepath, $filename)
return $filepath
For the -Return flag, you can actually return values directly from the foreach into an array and then return that 😄
# save the files
$filePathsList = @(foreach ($file in $files) {
# logic
if ($Return) {
$filePath
}
})
if ($Return) {
return $filePathsList
}
Saves building list objects and appending to them 😉