PowerShell脚本上传O365用户照片后遇ConvertTo-Json内存不足异常求助
问题与解决方案:PowerShell上传O365用户照片时的内存溢出异常
问题背景
写了一段PowerShell脚本,功能是检查O365用户是否已存在头像;如果不存在,就检查本地是否有对应用户的照片文件,有则用Set-UserPhoto上传(当前环境没有Set-MgUserPhotoContent权限)。但上传5-6张照片后,会抛出ConvertTo-Json : Exception of type 'System.OutOfMemoryException' was thrown错误,还需要保证脚本末尾能正常生成报告。之前试过用[System.IO.MemoryStream]::new()替代ReadAllBytes,但适配不了Set-UserPhoto命令。
原脚本代码:
foreach ($Object in $PictureStatus) { $Name = $Object.DisplayName $Picture= Get-MgUserPhoto -UserID $Object.UserPrincipalName -erroraction 'silentlycontinue' Write-Progress -Activity 'Processing' -CurrentOperation $name -PercentComplete (($counter / $picturestatus.count) * 100) Start-Sleep -Milliseconds 200 if ($Picture -ne $Null){ #has a picture in O365. $Properties = [ordered]@{'UserName'=$Name;'Picture Present'='True';'Picture on File'='NA';'Picture Uploaded'='False'} $Output_365Report += New-Object -TypeName PSObject -Property $Properties }Else { $TempVari=Get-ChildItem -Filter "$Name*" -File -Path $PhotoDatabase | where Length -gt 14kb #Write-Output $TempVari if ($TempVari -eq $null){ #no Picture in Database $Properties = [ordered]@{'UserName'=$Name;'Picture Present'='False';'Picture on File'='False';'Picture Uploaded'='False'} $Output_365Report += New-Object -TypeName PSObject -Property $Properties } else{ #Photo Upload to 365 Set-UserPhoto -Identity $Object.SamAccountName -PictureData ([System.IO.File]::ReadAllBytes("$PhotoDatabase"+"\$TempVari")) -Confirm:$False $Properties = [ordered]@{'UserName'=$Name;'Picture Present'='False';'Picture on File'='True';'Picture Uploaded'='True'} $Output_365Report += New-Object -TypeName PSObject -Property $Properties } }
异常原因分析
- 数组累加的内存浪费:用
$Output_365Report += ...的方式每次都会创建新数组,旧数组不会被及时回收,随着处理用户增多,内存占用持续上涨。 - 图片字节数组未及时释放:
[System.IO.File]::ReadAllBytes()会把整张图片一次性读进内存,且用完后没有主动释放,多张图片累积后触发内存溢出。 - 未清理临时变量:循环中的
$Picture、$TempVari等变量在循环结束后仍占用内存,没有及时回收。 - 路径拼接错误:原代码中
"$PhotoDatabase"+"\$TempVari"的写法,因$TempVari是FileInfo对象,可能生成无效路径,间接引发额外内存开销或错误。
解决方案与优化代码
关键优化点
- 用
System.Collections.ArrayList替代普通数组存储报告数据,避免每次重建数组的内存开销。 - 读取图片后主动释放字节数组内存,配合垃圾回收减少内存占用。
- 循环结束后清理临时变量,避免内存泄漏。
- 修正路径处理逻辑,直接使用文件对象的
FullName属性获取有效路径。 - 增加异常捕获,避免上传失败导致脚本中断,同时记录失败状态。
优化后的脚本
# 初始化ArrayList存储报告,替代原数组(避免每次重建数组的内存浪费) $Output_365Report = New-Object System.Collections.ArrayList $counter = 0 foreach ($Object in $PictureStatus) { $counter++ $Name = $Object.DisplayName Write-Progress -Activity 'Processing' -CurrentOperation $Name -PercentComplete (($counter / $PictureStatus.Count) * 100) # 检查O365头像,先清空变量再赋值,避免残留旧数据 $Picture = $null $Picture = Get-MgUserPhoto -UserID $Object.UserPrincipalName -ErrorAction 'SilentlyContinue' if ($Picture) { # 用户已有O365头像 $Properties = [ordered]@{ 'UserName' = $Name 'Picture Present' = 'True' 'Picture on File' = 'NA' 'Picture Uploaded' = 'False' } # 用[void]抑制Add方法返回的索引值,避免干扰输出 [void]$Output_365Report.Add((New-Object -TypeName PSObject -Property $Properties)) } else { # 查找本地照片,修正过滤逻辑的写法 $TempVari = Get-ChildItem -Filter "$Name*" -File -Path $PhotoDatabase | Where-Object { $_.Length -gt 14KB } if (-not $TempVari) { # 本地无对应照片 $Properties = [ordered]@{ 'UserName' = $Name 'Picture Present' = 'False' 'Picture on File' = 'False' 'Picture Uploaded' = 'False' } [void]$Output_365Report.Add((New-Object -TypeName PSObject -Property $Properties)) } else { # 上传照片,优化内存使用 $pictureBytes = $null $pictureBytes = [System.IO.File]::ReadAllBytes($TempVari.FullName) $uploadStatus = 'True' try { Set-UserPhoto -Identity $Object.SamAccountName -PictureData $pictureBytes -Confirm:$False } catch { $uploadStatus = 'False' Write-Warning "上传$Name的照片失败:$_" } finally { # 主动触发垃圾回收,释放图片字节数组占用的内存 [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } $Properties = [ordered]@{ 'UserName' = $Name 'Picture Present' = 'False' 'Picture on File' = 'True' 'Picture Uploaded' = $uploadStatus } [void]$Output_365Report.Add((New-Object -TypeName PSObject -Property $Properties)) } } # 清理当前循环的临时变量,释放内存 Remove-Variable -Name Picture, TempVari, pictureBytes -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 200 } # 生成最终报告(示例为导出CSV,可根据需求调整格式) $Output_365Report | Export-Csv -Path ".\O365UserPhotoReport.csv" -NoTypeInformation -Encoding UTF8
内容的提问来源于stack exchange,提问作者IT Analyst UT
相关产品推荐
相关产品推荐

