2016-09-30 25 views
1

我試圖壓縮包含子文件夾和項目,文件夾使用Windows外殼CopyHere命令:如何壓縮Windows中1個語句中的文件夾內容?

https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms723207(v=vs.85).aspx

更新:注意,更喜歡本土solution--這是一個分佈式的Excel VBA工具,所以捆綁第三方文件並不理想。並且,需要同步壓縮。

我可以在文件夾輕鬆添加其內容的zip:

所以我們知道在1個語句 CopyHere 可以過程中文件夾內的多個對象。

問題是,上面的命令將包含文件夾放在zip的根目錄,它的內容是它的內容。但是,我不想要包含的文件夾 - 只是其內容。

的文檔提到一個通配符(選項128),但是當我使用通配符,我得到一個錯誤:

oShell.Namespace(sZipPath).CopyHere "C:\My Folder\*" 

The file name you specified is not valid or too long.

也許還有用我上面的第一個命令的方式,然後移動zip中的項目到zip的根目錄?

循環訪問源文件夾中的每個項目,每次添加一個到zip將是可接受的。但是,因爲CopyHere是異步的,所以如果之前的CopyHere沒有完成,每個後續的CopyHere都會失敗。該修復程序都不工作,針對此問題:

  • 比較在源文件夾和目標拉鍊項目數失敗,因爲如果壓縮包含一個文件夾,則計爲只有1個項目(其中包含的項目不計https://stackoverflow.com/a/16603850/209942

  • 等待一段時間之間的每一項工作,但計時器是不可接受的。這是任意的,我不能提前猜大小或壓縮時間每個對象的

  • 檢查,看看。如果我阻止我的循環,直到文件不是loc ked,我仍然遇到文件訪問錯誤。 https://stackoverflow.com/a/6666663/209942


Function FileIsOpen(sPathname As String) As Boolean ' true if file is open 
    Dim lFileNum As Long 
    lFileNum = FreeFile 
    Dim lErr As Long 
    On Error Resume Next 
    Open sPathname For Binary Access Read Write Lock Read Write As #lFileNum 
    lErr = Err 
    Close #lFileNum 
    On Error GoTo 0 
    FileIsOpen = (lErr <> 0) 
End Function 

更新: VBA可以調用殼同步命令(而不是創建在VBA一個shell32.shell對象),所以如果CopyHere工作在命令行或PowerShell的,這可能是解。正在調查......

+0

我會說你最好的選擇是堅持'CopyHere'並添加一個循環,等到一切都被添加到zip壓縮文件。在我的答案[here](http://stackoverflow.com/a/22050803/1630171)中包含函數中的代碼應該不會太困難。 –

+0

thx,@AnsgarWiechers,但正如我在我的OP中指出的那樣,「比較項目數量失敗,因爲如果zip包含一個文件夾,則僅計入1個項目 - 文件夾內的項目不計入。」那,我不喜歡投票循環。我已經解決了這個問題,並在下面發佈了一個高級(對我來說)解決方案。 –

+0

只有直接子項目(文件和文件夾)的數量是相關的,而不是在該級別下嵌套的項目數量。我用我的答案測試了這個確切場景中的代碼(壓縮包含文件和子文件夾的文件夾,其中包含一些相當大的文件),並且它按照原樣運行(意味着等待循環僅在添加子文件夾的所有子元素後才終止)。 –

回答

1

解決方案:

的Windows包含另一個本地壓縮實用程序:CreateFromDirectory在PowerShell提示符。

https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile.createfromdirectory(v=vs.110).aspx

https://blogs.technet.microsoft.com/heyscriptingguy/2015/03/09/use-powershell-to-create-zip-archive-of-folder/

這需要.NET 4.0或更高版本:

> Add-Type -AssemblyName System.IO.Compression 
> $src = "C:\Users\v1453957\documents\Experiment\rezip\aFolder" 
> $zip="C:\Users\v1453957\Documents\Experiment\rezip\my.zip" 
> [io.compression.zipfile]::CreateFromDirectory($src, $zip) 

注意,你可能必須提供完整的pathnames--活動目錄是不是我的機器上隱。

以上壓縮是同步在PowerShell提示符處,因爲OP請求。


下一步是從VBA同步執行。解決方法是Windows Script Host Object Model中的.Run方法。在VBA中,設置爲參考,並執行以下操作,設置.Run命令的第三個參數,bWaitOnReturnTrue

Function SynchronousShell(sCmd As String)As Long Dim oWSH As New IWshRuntimeLibrary.WshShell ShellSynch = oWSH.Run(sCmd, 3, True) Set oWSH = Nothing End Function

現在叫SynchronousShell,並通過它的整個壓縮腳本。

我相信這個過程工作的唯一方法是如果CreateFromDirectory在與Add-Type相同的會話中執行。

所以,我們必須把整個事情作爲一個字符串傳遞。也就是說,將所有4個命令加載到一個變量sCmd中,以使Add-Type與後續的CreateFromDirectory保持關聯。在PowerShell中的語法,你可以用;

https://thomas.vanhoutte.be/miniblog/execute-multiple-powershell-commands-on-one-line/

而且它們分開,你要使用單引號代替雙引號,周圍的其他琴絃雙引號被刪除時,菊花鏈命令傳遞給PowerShell。EXE

https://stackoverflow.com/a/39801732/209942

sCmd = "ps4 Add-Type -AssemblyName System.IO.Compression; $src = 'C:\Users\v1453957\documents\Experiment\rezip\aFolder'; $zip='C:\Users\v1453957\Documents\Experiment\rezip\my.zip'; [io.compression.zipfile]::CreateFromDirectory($src, $zip)" 

解決。以上構成了完整的解決方案。


額外的信息:下面附加註釋是特殊情況:

多版本的.Net環境

如果.NET < 4.0是你的操作系統上的活動環境,那麼System.IO.Compression不存在--命令將失敗。但是,如果你的機器有.NET 4個集可用,你仍然可以做到這一點:

  • 創建它運行PowerShell和.NET 4見https://stackoverflow.com/a/31279372

  • 在你Add-Type以上命令的批處理文件,使用.Net 4壓縮程序集的確切路徑。在我贏Server 2008的:

Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll"

便攜

事實證明,我的機器上,我可以壓縮DLL複製到任意文件夾,並在複製電話和其工作原理:

Add-Type -Path "C:\MyFunnyFolder\System.IO.Compression.FileSystem.dll"

我不知道發生了什麼要求EN確定這是有效的 - 它可能需要完整的.Net 4.0或2.0文件位於其預期的目錄中。我假設DLL調用其他.Net程序集。也許我們只是很幸運有這樣一個:)

字符限制

根據我們的路徑和文件名的深度,字符數可能是一個問題。 PowerShell可能有260個字符的限制(不確定)。

https://support.microsoft.com/en-us/kb/830473

https://social.technet.microsoft.com/Forums/windowsserver/en-US/f895d766-5ffb-483f-97bc-19ac446da9f8/powershell-command-size-limit?forum=winserverpowershell

由於.Run經過Windows外殼,你也不必擔心該字符的限制,但在8K +,這是一個有點更寬敞: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 https://stackoverflow.com/a/3205048/209942

網站下面提供了一個24k +字符的方法,但我還沒有研究它: http://itproctology.blogspot.com/2013/06/handling-freakishly-long-strings-from.html

至少,因爲我們可以將dll放在任何我們喜歡的位置,所以我們可以將它放在C:root附近的一個文件夾中,以減少字符數。

更新:This post顯示了我們如何將整個事情放在腳本文件中,並用ps4.cmd調用它。這可能會成爲我的首選答案:

.\ps4.cmd GC .\zipper.ps1 | IEX

- 根據答案here


CopyHere:

回覆的問題:能CopyHere命令在命令行中執行呢?

CopyHere可以直接在PowerShell提示符下執行(代碼如下)。但是,即使在PowerShell中它也是異步的 - 在過程完成之前,控制權返回到PowerShell提示符。因此,OP沒有解決方案。這是它是如何完成的:

> $shellapp=new-object -com shell.application 
> $zippath="test.zip" 
> $zipobj=$shellapp.namespace((Get-Location).Path + "\$zippath") 
> $srcpath="src" 
> $srcobj=$shellapp.namespace((Get-Location).Path + "\$srcpath") 
> $zipobj.Copyhere($srcobj.items()) 
1

自動化Shell對象並不是一個可行的方法,因爲你已經發現了。儘管如此,Explorer Shell並沒有以任何其他方式真正公開此功能,但至少在Windows Vista之前並未以任何易於從VB6程序或VBA宏使用的方式進行使用。

最好的選擇是第三方ActiveX庫,但要注意64位VBA主機,您需要64位版本的這樣的庫。

另一種選擇是獲取zlibwapi.dll的後續副本,並使用一些VB6包裝器代碼。這也是一個32位解決方案。

這就是Zipper & ZipWriter, Zipping from VB programs所做的。考慮到你的需求(出於某種原因,包括對Timer控件的恐懼),你可以使用同步的ZipperSync類。在那裏見#4。該代碼包含一個簡單的AddFolderToZipperSync捆綁邏輯來添加一個文件夾,而不是僅僅一個文件。

同步類的缺點是大的存檔操作凍結了您的程序UI直到它完成。如果你不想使用Zipper UserControl來代替。

你也可以從這個想法中編寫你自己的包裝類。

+0

Thx for options。不是定時器的「恐懼」 - 我不能預測壓縮時間,所以這只是一個猜測。我對嗎? 「同步課的缺點......大型檔案操作凍結了你的程序」-i *想*同步。但希望本地解決方案(添加到OP) - 這是一個分佈式Excel工具,因此不需要第三方組件。將VBA中的引用添加到zlibwapi.dll中會引發錯誤。 ZipperSync爲我的VB + 2文件添加了相當多的代碼(如果我明白的話)。如果我必須捆綁第三方,7z.exe給出了一個簡單的1-liner shell命令,我可以同步完成:http://superuser.com/a/94437 –

相關問題