Multiple files (same field name)
Accept multiple files posted under a single field name.
Full source
File: pwsh/tutorial/examples/22.2-Multiple-Files.ps1
<#!
22.2 multipart/form-data with multiple files under the same field name
Client example (PowerShell):
$Port = 5000
$client = [System.Net.Http.HttpClient]::new()
$content = [System.Net.Http.MultipartFormDataContent]::new()
$content.Add([System.Net.Http.StringContent]::new('Batch upload'),'note')
$file1 = [System.Net.Http.ByteArrayContent]::new([System.Text.Encoding]::UTF8.GetBytes('file-1'))
$file1.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('text/plain')
$content.Add($file1,'files','one.txt')
$file2 = [System.Net.Http.ByteArrayContent]::new([System.Text.Encoding]::UTF8.GetBytes('file-2'))
$file2.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('text/plain')
$content.Add($file2,'files','two.txt')
$resp = $client.PostAsync("http://127.0.0.1:$Port/upload", $content).Result
$resp.Content.ReadAsStringAsync().Result
Cleanup:
Remove-Item -Recurse -Force (Join-Path ([System.IO.Path]::GetTempPath()) 'kestrun-uploads-22.2-multiple-files')
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
New-KrLogger |
Set-KrLoggerLevel -Value Debug |
Add-KrSinkConsole |
Register-KrLogger -Name 'console' -SetAsDefault
New-KrServer -Name 'Forms 22.2'
Add-KrEndpoint -Port $Port -IPAddress $IPAddress | Out-Null
# Upload directory
$scriptName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath)
$uploadRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "kestrun-uploads-$scriptName"
New-KrFormPartRule -Name 'files' -Required -AllowedContentTypes 'text/plain' |
Add-KrFormOption -DefaultUploadPath $uploadRoot -ComputeSha256 |
Add-KrFormRoute -Pattern '/upload' -ScriptBlock {
$files = @($FormPayload.Files['files'])
$result = [pscustomobject]@{
count = $files.Count
files = $files | ForEach-Object {
[pscustomobject]@{
fileName = $_.OriginalFileName
length = $_.Length
sha256 = $_.Sha256
}
}
}
Write-KrJsonResponse -InputObject $result -StatusCode 200
}
Enable-KrConfiguration
# Start the server asynchronously
Start-KrServer
Step-by-step
- Logger: Register console logging for visibility.
- Server: Create and bind a listener.
- Storage: Configure a temporary upload directory.
- Options: Enable SHA-256 hashes for stored parts.
- Route: Add
/uploadwithAdd-KrFormRoute. - Response: Return the list of uploaded files and counts.
Try it
$client = [System.Net.Http.HttpClient]::new()
$content = [System.Net.Http.MultipartFormDataContent]::new()
$content.Add([System.Net.Http.StringContent]::new('batch'),'note')
$file1 = [System.Net.Http.ByteArrayContent]::new([System.Text.Encoding]::UTF8.GetBytes('one'))
$file1.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('text/plain')
$content.Add($file1,'files','one.txt')
$file2 = [System.Net.Http.ByteArrayContent]::new([System.Text.Encoding]::UTF8.GetBytes('two'))
$file2.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('text/plain')
$content.Add($file2,'files','two.txt')
$client.PostAsync('http://127.0.0.1:5000/upload', $content).Result.Content.ReadAsStringAsync().Result
curl -F "note=batch" -F "files=@one.txt" -F "files=@two.txt" http://127.0.0.1:5000/upload
Expected output
{
"count": 2,
"files": [
{ "fileName": "one.txt", "length": 3, "sha256": "..." },
{ "fileName": "two.txt", "length": 3, "sha256": "..." }
]
}
Notes
- Accessing files:
Add-KrFormRouteinjects a$FormPayloadvariable. Read files from$FormPayload.Files. - Multiple values: a single field name can map to multiple files (e.g.
$FormPayload.Files['files']). - Limits:
KrFormOptions.Limits.MaxPartsprotects against too many sections. - Security: Filenames are sanitized; uploads are stored in a temp directory.
- Logging: Each part is logged with metadata only through
host.Logger.
Troubleshooting
- 415 / Unsupported Content-Type: Ensure you post
multipart/form-data. - Missing files: Ensure you use the same field name (
files) for each file part.
References
Previous / Next
Previous: Basic multipart/form-data upload Next: application/x-www-form-urlencoded forms