概要
まっさらなWindows上でZipファイルを圧縮する方法として
- 対象ファイル/フォルダを右クリックし、送る→圧縮
- PowershellのCompress-Archiveコマンド
- ZipFile.CreateFromDirectory呼出
があります。それぞれの方法の内、どれを使えばよいか考えます。
なお、Shellオブジェクト経由のCopyHereを使用した圧縮方法は、
公式から非推奨な方法とされているため取り扱いません。
結論
- 更新日時を秒単位で保持したいのであれば、標準機能では実現できない
- すべてのフォルダ/ファイルを圧縮したいのであれば、ZipFile一択
- "読取専用"や"隠し"等、ファイル属性を保持したいのなら、送る→圧縮
理由
更新日時に関してはどの方法もZipファイルの仕様(2秒刻みの日時でしか表現できない)に従い、また拡張フィールドに細かい日時の記録もしていないようです。
FATはともかくNTFSはより細かい単位で記録されるため、圧縮前のファイルと、圧縮後展開したファイルの更新日時はほとんどの場合一致しません。
送る→圧縮に関して
「.」で始まるフォルダが複数ある場合、1個目のフォルダしか圧縮されまぜん。
例えば、下図のように「.dir」フォルダ「.dir2」フォルダを含むフォルダを圧縮すると、「.dir2」フォルダは(その中に含まれるファイルフォルダを含めて)無視されます。
PowershellのCompress-Archiveコマンド
- ファイル属性は保存されない
- 隠しファイルや隠しフォルダは無視される
- パラメータの指定方法によっては空フォルダが無視される
- 2GBの制約
ZipFile.CreateFromDirectory
- ファイル属性は保存されない
[おまけ] すべてのフォルダ/ファイルを圧縮するスクリプト
ファイルの属性は保存できず。また、タイムスタンプも2秒刻みですが、
以下のスクリプトなら、Win10で安全にZipファイルを作成できます。
@Echo Off
setlocal
Set TargetPath=%~dpnx1
Set DestPath=%~dpnx2
Rem 第2引数がセットされていないなら、第1引数から算出
If "%DestPath%" equ "" Set DestPath=%~dpn1.zip
Rem パラメタ未設定はエラー
If "%TargetPath%" equ "" Goto NoParamError
Echo 圧縮対象: %TargetPath%
Echo 圧縮ファイル: %DestPath%
Rem 圧縮ファイルが対象が既に存在するならエラー
If exist "%DestPath%" Goto AlreadyExistError
Rem 圧縮対象がファイルかフォルダかで実行コマンドを変更
If exist "%TargetPath%\" Goto DoCompressDirectory
If exist "%TargetPath%" Goto DoCompressFile
Goto TargetNotExistError
:NoParamError
Set Message=圧縮するファイル/フォルダを入力してください。
PowerShell -Command "Write-Host -fore Red %Message%" >&2
Exit /B 1
:AlreadyExistError
Set Message=圧縮ファイルはすでに存在します。
PowerShell -Command "Write-Host -fore Red %Message%" >&2
Exit /B 2
:TargetNotExistError
Set Message=圧縮するファイル/フォルダが見つかりません。
PowerShell -Command "Write-Host -fore Red %Message%" >&2
Exit /B 3
:DoCompressFile
PowerShell -Command "Add-Type -AssemblyName System.IO.Compression ; Add-Type -AssemblyName System.IO.Compression.FileSystem ; $za=[System.IO.Compression.ZipFile]::Open('%DestPath%', [System.IO.Compression.ZipArchiveMode]::Create) ; Try{ $entryNm=[System.IO.Path]::GetFileName('%TargetPath%') ; [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($za, '%TargetPath%', $entryNm) } Finally { $za.Dispose() }"
Goto Finally
:DoCompressDirectory
PowerShell -Command "Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::CreateFromDirectory('%TargetPath%', '%DestPath%')"
Goto Finally
:Finally
Exit /B %errorlevel%