脚本目的
该脚本背后的想法是从大量文档中递归提取文本,并使用提取的文本更新Azure SQL数据库中的字段。基本上,我们正在从文档内容的Windows搜索转移到SQL全文搜索以提高速度。
问题
当脚本遇到打开文件的问题(例如受密码保护)时,此脚本对于随后的每个文档都会失败。这是脚本中处理文件的部分:
foreach ($list in (Get-ChildItem ( join-path $PSScriptRoot "\FileLists\*" ) -include *.txt )) {
## Word object
$word = New-Object -ComObject word.application
$word.Visible = $false
$saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatText")
$word.DisplayAlerts = 0
Write-Output ""
Write-Output "################# Parsing $list"
Write-Output ""
$query = "INSERT INTO tmp_CachedText (tCachedText, tOID)
VALUES "
foreach ($file in (Get-Content $list)) {
if ($file -like "*-*" -and $file -notlike "*~*") {
Write-Output "Processing: $($file)"
Try {
$doc = $word.Documents.OpenNoRepairDialog($file, $false, $false, $false, "ttt")
if ($doc) {
$fileName = [io.path]::GetFileNameWithoutExtension($file)
$fileName = $filename + ".txt"
$doc.SaveAs("$env:TEMP\$fileName", [ref]$saveFormat)
$doc.Close()
$4ID = $fileName.split('-')[-1].replace(' ', '').replace(".txt", "")
$text = Get-Content -raw "$env:TEMP\$fileName"
$text = $text.replace("'", "''")
$query += "
('$text', $4ID),"
Remove-Item -Force "$env:TEMP\$fileName"
<# Upload to azure #>
$query = $query.Substring(0,$query.Length-1)
$query += ";"
Invoke-Sqlcmd @params -Query $Query -ErrorAction "SilentlyContinue"
$query = "INSERT INTO tmp_CachedText (tCachedText, tOID)
VALUES "
}
}
Catch {
Write-Host "$($file) failed to process" -ForegroundColor RED;
continue
}
}
}
Remove-Item -Force $list.FullName
Write-Output ""
Write-Output "Uploading to azure"
Write-Output ""
<# Upload to azure #>
Invoke-Sqlcmd @params -Query $setQuery -ErrorAction "SilentlyContinue"
$word.Quit()
TASKKILL /f /PID WINWORD.EXE
}
基本上,它将分析包含x个文档路径的.txt文件文件夹,创建一个T-SQL更新语句,并在完全解析每个文件之后针对Azure SQL数据库运行。这些文件的生成如下:
if (!($continue)) {
if ($pdf){
$files = (Get-ChildItem -force -recurse $documentFolder -include *.pdf).fullname
}
else {
$files = (Get-ChildItem -force -recurse $documentFolder -include *.doc, *.docx).fullname
}
$files | Out-File (Join-Path $PSScriptRoot "\documents.txt")
$i=0; Get-Content $documentFile -ReadCount $interval | %{$i++; $_ | Out-File (Join-Path $PSScriptRoot "\FileLists\documents_$i.txt")}
}
$ interval 变量定义为给定的每次上传到Azure提取多少个文件。最初,我在循环之外创建了单词对象,直到结束都没有关闭过。不幸的是,这似乎不起作用,因为脚本每次击中一个无法打开的文件时,随后的每个文件都会失败,直到到达内部foreach循环foreach ($file in (Get-Content $list)) {
的末尾。
这意味着要获得预期的结果,我必须以1的间隔运行它,这花费了太长时间。
答案 0 :(得分:1)
这是在黑暗中的镜头
但是对我来说,这听起来像是失败的原因是因为Word Com对象现在由于无法打开文件而提示您执行某些操作,因此循环中的所有后续项也会失败。这可能可以解释为什么将$Interval
设置为1的原因,因为当PowerShell
设置为1时,它每次都会关闭并打开Com对象,并且这要花很多时间(我用excel做到了)。
您可以做的是在catch语句中,关闭并打开一个新的Word Com对象,该对象应该使您继续循环(但是如果需要大量打开Com对象,它会慢一些)。
如果您想进一步调试问题,请将Com对象设置为可见,然后在不与Word交互的情况下缓慢遍历程序。这将向您显示Word发生了什么以及是否有任何提示导致应用程序挂起。
当然,如果要全速运行它,则需要先检测无法打开的文档,或者可以通过打开多个Word Com对象来对其进行多线程处理,从而可以在以下位置加载多个文档时间。
答案 1 :(得分:0)
至于...
问题
当脚本遇到打开文件的问题(例如受密码保护)时,此脚本对于随后的每个文档都会失败。
...然后按照此处所述进行测试...
How to check if a word file has a password?
$filename = "C:\path\to\your.doc"
$wd = New-Object -COM "Word.Application"
try {
$doc = $wd.Documents.Open($filename, $null, $null, $null, "")
} catch {
Write-Host "$filename is password-protected!"
}
...并跳过文件以避免剩余文件失败。