PowerShell HttpListener接收multipart/form-data上传文件如何保存到本地
PowerShell HttpListener 处理multipart/form-data文件上传解决方案
错误根因
- 你当前使用的
StreamReader.ReadToEnd()方法会把二进制格式的请求流转换为字符串,图片这类二进制文件转字符串会发生不可逆的编码损坏,同时字符串类型也无法直接传入要求字节数组参数的WriteAllBytes方法 - 当前请求的
ContentType为multipart/form-data类型,请求体中不止包含文件二进制内容,还包含表单边界、Content-Disposition元数据等信息,直接写入文件肯定无法得到可用的文件 - 你原有的
Invoke-GetBody函数仅适用于读取文本格式的请求体,不适合处理二进制文件上传场景
正确实现代码
你需要直接读取原始字节流,解析multipart表单结构提取文件内容,完整实现逻辑如下:
# 辅助函数:在字节数组中查找指定字节序列的偏移位置 function Find-Bytes { param( [byte[]]$Bytes, [byte[]]$Pattern, [int]$StartOffset = 0 ) for ($i = $StartOffset; $i -le $Bytes.Length - $Pattern.Length; $i++) { $match = $true for ($j = 0; $j -lt $Pattern.Length; $j++) { if ($Bytes[$i + $j] -ne $Pattern[$j]) { $match = $false break } } if ($match) { return $i } } return -1 } # 1. 从请求头提取表单边界 $boundary = $script:Request.ContentType.Split('=')[1].Trim() $boundaryBytes = [System.Text.Encoding]::ASCII.GetBytes("--$boundary") # 2. 读取完整的原始请求字节流,避免编码损坏 $memoryStream = New-Object System.IO.MemoryStream $script:Request.InputStream.CopyTo($memoryStream) $allBytes = $memoryStream.ToArray() $memoryStream.Dispose() $script:Request.InputStream.Dispose() # 3. 定位文件内容的起止位置 $firstBoundaryIndex = Find-Bytes -Bytes $allBytes -Pattern $boundaryBytes if ($firstBoundaryIndex -eq -1) { throw "无效的multipart表单:未找到边界" } $headerEndPattern = [System.Text.Encoding]::ASCII.GetBytes("`r`n`r`n") $headerEndIndex = Find-Bytes -Bytes $allBytes -Pattern $headerEndPattern -StartOffset ($firstBoundaryIndex + $boundaryBytes.Length) if ($headerEndIndex -eq -1) { throw "无效的multipart表单:未找到表头结束标识" } $nextBoundaryIndex = Find-Bytes -Bytes $allBytes -Pattern $boundaryBytes -StartOffset ($headerEndIndex + $headerEndPattern.Length) if ($nextBoundaryIndex -eq -1) { throw "无效的multipart表单:未找到结束边界" } # 4. 提取文件字节数组 $fileContentLength = $nextBoundaryIndex - $headerEndIndex - $headerEndPattern.Length - 2 # 减去文件结尾的\r\n $fileBytes = New-Object byte[] $fileContentLength [Array]::Copy($allBytes, $headerEndIndex + $headerEndPattern.Length, $fileBytes, 0, $fileContentLength) # 5. 保存到本地文件 [System.IO.File]::WriteAllBytes('C:\test.png', $fileBytes)
扩展说明
- 如果你的表单包含多个字段或者多个文件,可以循环查找边界,逐个解析每个区块的
Content-Disposition头,匹配filename="xxx"规则判断是否为文件字段,同时提取原始文件名 - 如果需要支持中文文件名,需要额外处理
Content-Disposition的编码适配逻辑
内容的提问来源于stack exchange,提问作者CrommCruach
相关产品推荐
相关产品推荐

