PowerShell上传SharePoint遇400错误:如何兼容Zip文件与普通文件夹?
我来帮你搞定这个问题!你的代码出现400错误主要是三个核心问题:错误的请求头、不分类型的强制压缩,以及硬编码的文件名后缀。下面是修复后的完整代码,我会在后面解释关键改动:
param($ClientID,$ClientSecret,$WId,$RName,$RReqFor,$SPid,$LibId) $tempDirectory = "temp" # arguments defined from predefined variables in argument section of powershell stage of pipeline $workingDirectory = "$WId" $CLIENT_ID = "$ClientID" $CLIENT_SECRET = "$ClientSecret" $SharePointSiteId = "$SPid" $LibraryId = "$LibId" $azx = "$RName"+"_"+"$RReqFor" $adx = ($azx.Split("[")[0]) $artifactname = $adx.Substring(0,$adx.Length-1) # Get access token Function GET-TOKEN { param($clientId, $clientSecret) $tokenHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $tokenHeaders.Add("Content-Type", "application/x-www-form-urlencoded") $tokenBody = "client_id="+ $clientId + "&scope=https%3A//graph.microsoft.com/.default&client_secret=" + $clientSecret + "&grant_type=client_credentials" $tokenResponse = Invoke-RestMethod 'https://login.microsoftonline.com/test.onmicrosoft.com/oauth2/v2.0/token' -Method 'POST' -Headers $tokenHeaders -Body $tokenBody return $tokenResponse } # upload the file Function UPLOAD-FILE { param($workingDir, $tempDir, $clientId, $clientSecret) Get-ChildItem $workingDir | ForEach-Object { $item = $_ $uploadFileName = $item.Name $fileToUpload = $item.FullName # 处理文件夹:压缩成Zip if ($item.PSIsContainer) { $uploadFileName = "$($item.Name).zip" $zipPath = Join-Path $item.FullName $tempDir | Join-Path -ChildPath $uploadFileName CREATE-ARCHIVE -sourceDir $item.FullName -zipOutputPath $zipPath $fileToUpload = $zipPath } # 处理Zip文件:直接使用原文件;非Zip文件:直接上传原文件(如需压缩可在此添加逻辑) # 获取文件信息 $fileSize = (Get-Item $fileToUpload).Length $tokenObject = GET-TOKEN -clientId $clientId -clientSecret $clientSecret # 获取上传链接 $uploadURLObject = GET-UPLOADLINK -clientId $clientId -clientSecret $clientSecret -targetFileName $uploadFileName # 构建正确的上传请求头 $uploadHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $uploadHeaders.Add("Content-Type", "application/octet-stream") # 通用二进制类型,兼容所有文件 $uploadHeaders.Add("Content-Range", "bytes 0-$($fileSize-1)/$fileSize") $uploadHeaders.Add("Content-Length", $fileSize) $uploadHeaders.Add("Authorization", "Bearer "+ $tokenObject.access_token) # 读取文件内容并上传 $uploadBody = [System.IO.File]::ReadAllBytes($fileToUpload) try { $response = Invoke-RestMethod $uploadURLObject.uploadUrl -Method 'PUT' -Headers $uploadHeaders -Body $uploadBody Write-Host "Successfully uploaded: $uploadFileName" $response | ConvertTo-Json } catch { Write-Error "Failed to upload $uploadFileName : $_" } # 清理临时目录(仅当处理文件夹时) if ($item.PSIsContainer) { REMOVE-TEMPDIR -workingDir $item.FullName -tempDir $tempDirectory } } } Function GET-UPLOADLINK { param($clientId, $clientSecret, $targetFileName) $tokenObject = GET-TOKEN -clientId $clientId -clientSecret $clientSecret $uploadLinkRequestHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $uploadLinkRequestHeaders.Add("Content-Type", "application/json") $uploadLinkRequestHeaders.Add("Authorization", "Bearer "+ $tokenObject.access_token) $uploadLinkRequestBody = @{} | ConvertTo-Json # 动态生成上传路径,不再硬加.zip后缀 $fullname = "https://graph.microsoft.com/v1.0/sites/$($SharePointSiteId)/drives/$($LibraryId)/root/children/$targetFileName/createUploadSession" $uploadLinkResponse = Invoke-RestMethod $fullname -Method 'POST' -Headers $uploadLinkRequestHeaders -Body $uploadLinkRequestBody return $uploadLinkResponse } Function CREATE-ARCHIVE { param($sourceDir, $zipOutputPath) Write-Host "Compressing folder: $sourceDir to $zipOutputPath" # 创建临时目录(如果不存在) $tempDir = Split-Path $zipOutputPath -Parent if (-not (Test-Path $tempDir)) { New-Item -ItemType Directory -Path $tempDir | Out-Null } # 压缩文件夹内的所有内容,避免把父目录本身作为根目录 Compress-Archive -Path "$sourceDir\*" -DestinationPath $zipOutputPath -Force } Function REMOVE-TEMPDIR { param($workingDir, $tempDir) if (Test-Path "$workingDir\$tempDir") { Remove-Item "$workingDir\$tempDir" -Recurse -Force Write-Host "Cleaned temporary directory: $workingDir\$tempDir" } } UPLOAD-FILE -workingDir $workingDirectory -tempDir $tempDirectory -clientId $CLIENT_ID -clientSecret $CLIENT_SECRET
关键改动说明
区分文件与文件夹处理
- 使用
$item.PSIsContainer判断当前项是文件夹还是文件:- 文件夹:压缩成Zip后上传
- 文件:直接上传(Zip文件不二次压缩,非Zip文件原样上传)
- 如果需要压缩非Zip文件,你可以在文件处理分支里添加类似文件夹的压缩逻辑。
- 使用
修正Content-Type请求头
- 把原来错误的
application/json改成application/octet-stream,这是通用的二进制文件MIME类型,兼容所有文件格式,SharePoint能正确识别。
- 把原来错误的
动态生成上传文件名
- 修改
GET-UPLOADLINK函数,接受targetFileName参数,不再硬加.zip后缀,确保上传的文件名和实际文件一致(比如原Zip文件上传时不会变成xxx.zip.zip)。
- 修改
优化压缩逻辑
- 将
Compress-Archive的路径从$workingDir改成$sourceDir\*,这样只会压缩文件夹内的内容,而不是把文件夹本身作为根目录打包,生成的Zip结构更合理。 - 添加了临时目录的存在性检查,避免创建目录时出错。
- 将
增加错误处理
- 在上传步骤添加
try/catch块,方便排查单个文件上传失败的问题,不会因为一个文件出错导致整个脚本终止。
- 在上传步骤添加
内容的提问来源于stack exchange,提问作者Ankit Kumar
相关产品推荐
相关产品推荐

