如何在PowerShell中复制图片到剪贴板时保留其文件大小?
解决压缩PNG复制到剪贴板后体积反弹的问题
我懂你碰到的这个麻烦:用pngquant把PNG从3MB压到760KB,结果不管是用你那行PowerShell脚本还是Windows自带的图片查看器复制到剪贴板,体积又变回2MB了。这事儿根源很清楚——Windows剪贴板的SetImage()方法(包括系统查看器背后用的逻辑)会默认把加载的图片转换成未压缩的Bitmap格式存进剪贴板,相当于把你辛辛苦苦压缩的成果又还原回去了。
下面给你两种PowerShell的解决方案,能让剪贴板完完整整保留住pngquant压缩后的PNG数据:
方案1:兼顾文件粘贴和图片粘贴的全能版
这个方法会同时把压缩后的PNG文件和它的原生图片数据放到剪贴板,既能粘贴成文件,也能在支持PNG的应用里直接粘贴为图片,而且体积完全和压缩后的文件一致:
# 加载必要的.NET程序集 [Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null # 替换为你的实际文件路径(保留原%input变量逻辑) $inputPath = "%input" # 自动生成压缩后的文件名(和你原来的逻辑一致) $compressedPath = ($inputPath -split '\.', 2 | ForEach-Object { $_ + '_compressed.' })[0] + ($inputPath -split '\.', 2)[1] # 创建数据对象,先添加文件拖放格式 $dataObj = New-Object System.Windows.Forms.DataObject $fileList = New-Object System.Collections.Specialized.StringCollection $fileList.Add($compressedPath) $dataObj.SetFileDropList($fileList) # 再添加原生PNG格式的数据,确保粘贴为图片时用压缩后的版本 $pngRawBytes = [System.IO.File]::ReadAllBytes($compressedPath) $dataObj.SetData([System.Windows.Forms.DataFormats]::Png, $pngRawBytes) # 把数据对象放到剪贴板,第二个参数设为$true让数据在脚本结束后仍保留 [System.Windows.Forms.Clipboard]::SetDataObject($dataObj, $true)
方案2:仅专注于图片粘贴的轻量版
如果你不需要粘贴成文件,只需要把压缩后的图片贴到其他应用里,用这个更简洁的版本就行:
[Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null $inputPath = "%input" $compressedPath = ($inputPath -split '\.', 2 | ForEach-Object { $_ + '_compressed.' })[0] + ($inputPath -split '\.', 2)[1] # 直接读取压缩后的PNG二进制数据,以原生PNG格式写入剪贴板 $pngBytes = [System.IO.File]::ReadAllBytes($compressedPath) $pngData = New-Object System.Windows.Forms.DataObject([System.Windows.Forms.DataFormats]::Png, $pngBytes) [System.Windows.Forms.Clipboard]::SetDataObject($pngData, $true)
原理说明
你原来的脚本用[System.Drawing.Image]::FromFile()加载图片,再用SetImage()存到剪贴板——这个过程中,.NET会把PNG解码成未压缩的Bitmap,然后再用Bitmap的格式存进剪贴板,自然体积就变大了。而上面的方法直接读取pngquant压缩好的PNG文件的原始二进制数据,不经过解码步骤,直接以原生PNG格式写入剪贴板,完美保留了压缩后的体积和质量。
你可以测试一下:用这两个脚本复制后,在Photoshop、微信、Discord这类支持原生PNG粘贴的应用里,图片的体积就是你压缩后的760KB,不会再膨胀了。
内容的提问来源于stack exchange,提问作者keke




