【问题标题】:Powershell Writing to .XLSX is Corrupting the FilesPowershell 写入 .XLSX 会损坏文件
【发布时间】:2020-01-13 19:14:05
【问题描述】:

我有一个 Powershell 脚本,它循环遍历文件夹中的 .xslx 文件,密码用文件名保护它们(目前)。循环和写入 .xls 没有问题,但是当我尝试打开 .使用 Powershell 编写后的 xlsx 文件 - 我收到错误:

Excel 无法打开文件“abcd.xlsx”,因为文件格式或文件 扩展无效。验证文件没有损坏 并且文件扩展名与文件格式匹配。

这是脚本:

function Release-Ref ($ref) { 
    ([System.Runtime.InteropServices.Marshal]::ReleaseComObject( 
    [System.__ComObject]$ref) -gt 0) 
    [System.GC]::Collect() 
    [System.GC]::WaitForPendingFinalizers()  
    } 

$e = $ErrorActionPreference
$ErrorActionPreference="continue"
foreach ($f in Get-ChildItem "C:"){
    try{
        $ff = $f
        $xlNormal = -4143 
        $s = [System.IO.Path]::GetFileNameWithoutExtension($f)
        $xl = new-object -comobject excel.application 
        $xl.Visible = $False
        $xl.DisplayAlerts = $False   
        $wb = $xl.Workbooks.Open($ff.FullName)
        $wb.sheets(1).columns("A:S").entirecolumn.AutoFit()
        $wb.sheets(1).columns("N").NumberFormat = "0.0%"
        $a = $wb.SaveAs("C:\Out\" + $s + ".xls",$xlNormal,$s) #works
        #$a = $wb.SaveAs("C:\Out\" + $s + ".xlsx",$xlNormal,$s) #doesn't work
        $a = $xl.Quit() 

        $a = Release-Ref($ws) 
        $a = Release-Ref($wb) 
        $a = Release-Ref($xl) 
    }
    catch {
        Write-Output "Exception"
        $ErrorActionPreference=$e;
    }
}

我搜索了其他问题,但找不到从 Powershell 编写的相同问题的任何其他示例。谢谢。

【问题讨论】:

  • xls != xlsx。前者是二进制格式,后者是包含 XML 的 zip。尝试另存为 xlsx,但使用 file format value 51
  • 我没有考虑过这部分 - 非常感谢您的观点和解释。就是这样 - 如果您想添加答案,我会接受,谢谢!

标签: powershell loops xlsx xls


【解决方案1】:

问题是因为 Xls 与 Xlsx 的格式不同。 2007 版之前的旧 Excel 使用二进制格式。 2007 Office 引入了 Xslx 使用的new formats called Office Open Xml

Excel 足够聪明,可以检查文件扩展名和文件格式。由于保存带有新版本扩展名的二进制文件会产生冲突,因此错误消息也提示了这种可能性:

并且文件扩展名与文件格式匹配。

为什么 Excel 不直接打开文件?我想这是一项安全功能,可以防止无意打开 Office 文档。在过去,Office macro viruses 是许多办公室的祸根。主要的感染媒介之一是诱骗用户在没有预防措施的情况下打开文件。与经典病毒不同,宏病毒会感染应用程序数据(包括默认模板文件)而不是操作系统二进制文件,但这是另一回事了。

无论如何,要以正确的格式工作,请使用proper version value。那将是 -4143 对于 Xls 和 51 对于 Xlsx。更重要的是,Get-ChildItem 返回FileInfo 对象的集合,文件扩展名在Extension 属性中。像这样,

# Define Xls and Xlsx versions
$typeXls = -4143
$typeXls = 51
foreach ($f in Get-ChildItem "C:"){

    try{
    $ff = $f
    ...
   # Select saveas type to match original file extension
   if($f.extension -eq '.xsl') { $fType = $typeXls }
   else if($f.extension -eq '.xslx') { $fType = $typeXlsx }

   $a = $wb.SaveAs("C:\Out\" + $s + $.extension, $fType, $s)

【讨论】:

    【解决方案2】:

    使用 excel 有时处理 com 对象过于复杂。我推荐import-excel 模块。 Install-Module -Name ImportExcel

    然后你可以做这样的事情。

    function Release-Ref ($ref) { 
        $e = $ErrorActionPreference
        $ErrorActionPreference="continue"
    
    foreach ($f in Get-ChildItem $file){
        try{
            $filePass = gci $f
            $path = split-path $f
            $newFile = $path + "\" + $f.BaseName + "-protected.xlsx"
            $f | Export-excel $newFile -password $filePass -NoNumberConversion * -AutoSize
        }
        catch {
            Write-Output "Exception"
            $ErrorActionPreference=$e;
        }
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-09
      相关资源
      最近更新 更多