SharePoint Online列表附件下载后损坏无法打开,求排查PnP PowerShell脚本问题
我有一个SharePoint Online列表,里面的项目带有附件。我想用PnP PowerShell下载所有列表项的附件,写了如下脚本:
$ApprovalListItems = Get-PnPListItem -List "tickets" -PageSize 1000 -ScriptBlock { Param($items) $items.Context.ExecuteQuery()} | ForEach-Object { $ctx = Get-PnPContext $spSourceWeb= Get-PnPWeb $ItemAttachmentURLPrefix = 'https://****.sharepoint.com/Lists/tickets/Attachments/'+$_['ID']+'/' $attachmentCollection = ForEach-Object{Get-PnPProperty -ClientObject $_ -Property "AttachmentFiles"} $AttachmentSeq = 0 ForEach($Attachment in $attachmentCollection) { Write-Host "`tDownloading Attachement: " $attachment $ItemAttachmentURLPrefix $AttachmentSeq++ $AttachmentDataObj = "" | Select "Attachment Seq", "JDE Company", "Vendor Number", "Vendor Invoice Number", "Attachment Name", "Attachment System Name", "Job-run Date/Time Stamp" Write-Host $Attachment.FileName $file = Get-PnPFile -Url ($ItemAttachmentURLPrefix + $Attachment.FileName) $bytes = (Get-PnPFile -Url ($ItemAttachmentURLPrefix + $Attachment.FileName)).OpenBinaryStream() $name = "C:\Attachments\"+$Attachment.FileName $fs = New-Object System.IO.StreamWriter($name, "OpenOrCreate") $fs.Write($bytes, 0 , $bytes.Length) $fs.Close() $stream = $streamResult.Value Write-Host $file.Name Write-Host $stream.Name } }
但保存后的文件无法打开,Excel文件报错,PDF显示损坏。请问脚本哪里有问题?
脚本存在的核心问题
- 二进制文件处理错误:使用
StreamWriter写入二进制数据,StreamWriter是专门处理文本的工具,会将二进制字节按文本编码转换,直接破坏文件结构。 OpenBinaryStream调用错误:该方法返回的是ClientResult<Stream>对象,不是直接可用的字节数组或流,你没有正确提取其中的流数据。- 冗余的API调用:重复调用
Get-PnPFile获取同一个文件,既浪费资源也容易引发错误。 - 附件集合赋值错误:
ForEach-Object{Get-PnPProperty...}语法错误,不需要嵌套ForEach-Object,直接调用Get-PnPProperty即可获取附件集合。
修正后的脚本
# 确保下载目录存在 $downloadPath = "C:\Attachments" if (-not (Test-Path $downloadPath)) { New-Item -ItemType Directory -Path $downloadPath | Out-Null } # 获取列表项并遍历处理附件 Get-PnPListItem -List "tickets" -PageSize 1000 -ScriptBlock { Param($items) $items.Context.ExecuteQuery()} | ForEach-Object { $listItem = $_ # 获取当前列表项的附件集合 $attachmentCollection = Get-PnPProperty -ClientObject $listItem -Property "AttachmentFiles" if ($attachmentCollection.Count -gt 0) { Write-Host "处理列表项ID: $($listItem['ID']) 的附件" $AttachmentSeq = 0 foreach ($attachment in $attachmentCollection) { $AttachmentSeq++ Write-Host "`t下载附件: $($attachment.FileName)" # 获取附件的文件流 $fileStream = Get-PnPFile -Url $attachment.ServerRelativeUrl -AsFileStream $savePath = Join-Path -Path $downloadPath -ChildPath $attachment.FileName # 安全写入二进制文件 using ($localStream = [System.IO.File]::Create($savePath)) { $fileStream.CopyTo($localStream) } Write-Host "`t已保存到: $savePath" } } }
关键修复点说明
- 使用
-AsFileStream参数:直接获取文件流,避免手动处理二进制数据的编码错误。 - 二进制安全写入:用
File::Create创建文件流,通过CopyTo方法复制二进制数据,确保文件结构完整。 - 简化URL处理:直接使用附件的
ServerRelativeUrl属性,避免手动拼接URL的潜在错误。 - 自动管理流资源:
using语句会自动关闭并释放流资源,避免内存泄漏或文件占用问题。
内容的提问来源于stack exchange,提问作者John John
相关产品推荐
相关产品推荐

