.net - como - ¿Cómo puedo comprimir(/ zip) y descomprimir(/ descomprimir) archivos y carpetas con un archivo por lotes sin usar herramientas externas?
winrar (3)
Sé que aquí se hicieron muchas preguntas similares, pero no estoy completamente satisfecho con las respuestas (e incluso con las preguntas).
El objetivo principal es la compatibilidad: debe ser aplicable a la gama más amplia posible de máquinas Windows (incluidas XP, Vista, Win2003, que juntas aún contienen alrededor del 20% del uso compartido de Windows) y los archivos producidos deben ser utilizables en máquinas Unix / Mac (tan estándar los formatos de archivo / compresión son preferibles).
Cuáles son las opciones:
- Crear un lote que implemente algún algoritmo zip. Aparentemente, esto es posible, pero solo con archivos únicos y usando CERTUTIL para el procesamiento binario (algunas máquinas no tienen CERTUTIL por defecto y no se pueden instalar en WinXP Home Edition)
- Usando shell.application través de WSH. La mejor opción para mí. Permite comprimir directorios completos y puede usarse en todas las máquinas Windows
- Makecab : a pesar de que su compresión no es tan portátil, está disponible en todas las máquinas con Windows. Algunos programas externos como 7zip son capaces de extraer contenido .CAB, pero no será tan conveniente cuando los archivos deben usarse en Unix / Mac. Y mientras se comprime un solo archivo es bastante sencillo, preservar la estructura del directorio requiere un poco más de esfuerzo.
- Uso de .NET Framework: no es una buena opción. En el formato .NET 2.0 existe GZipStream pero solo permite la compresión de archivos individuales. .NET 4.5 tiene capacidades Zip pero no es compatible con Vista y XP. Y aún más: .NET no está instalado por defecto en XP y Win2003, pero como es muy probable que tenga .NET 2.0 hasta 4.0 es una opción considerable .
- Powershell: como se basa en .NET, tiene las mismas capacidades. No está instalado de forma predeterminada en XP, 2003 y Vista, así que lo omitiré.
Y aquí están las respuestas:
1.Utilizando un script por lotes "puro" para comprimir / descomprimir el archivo.
Es posible gracias a
ZIP.CMD
y
UNZIP.CMD
Frank Westlake (necesita permisos de administrador y requiere
FSUTIL
y
CERTUTIL
). Para Win2003 y WinXP requerirá
2003 Admin Tool Pack
que instalará
CERTUTIL
.
Tenga cuidado ya que la sintaxis ZIP.CMD es hacia atrás:
ZIP.CMD destination.zip source.file
Y puede comprimir solo archivos individuales.
2.Utilizando Shell.Aplicación
Pasé algún tiempo para crear un solo script híbrido jscript / batch para uso común que comprime / descomprime archivos y directorios (además de algunas características más).
Aquí hay un enlace
(se hizo demasiado grande para publicar en la respuesta).
Se puede usar directamente con su extensión
.bat
y no crea ningún archivo temporal.
Espero que el mensaje de ayuda sea lo suficientemente descriptivo de cómo se puede usar.
Algunos ejemplos:
// unzip content of a zip to given folder.content of the zip will be not preserved (-keep no).Destination will be not overwritten (-force no)
call zipjs.bat unzip -source C:/myDir/myZip.zip -destination C:/MyDir -keep no -force no
// lists content of a zip file and full paths will be printed (-flat yes)
call zipjs.bat list -source C:/myZip.zip/inZipDir -flat yes
// lists content of a zip file and the content will be list as a tree (-flat no)
call zipjs.bat list -source C:/myZip.zip -flat no
// prints uncompressed size in bytes
zipjs.bat getSize -source C:/myZip.zip
// zips content of folder without the folder itself
call zipjs.bat zipDirItems -source C:/myDir/ -destination C:/MyZip.zip -keep yes -force no
// zips file or a folder (with the folder itslelf)
call zipjs.bat zipItem -source C:/myDir/myFile.txt -destination C:/MyZip.zip -keep yes -force no
// unzips only part of the zip with given path inside
call zipjs.bat unZipItem -source C:/myDir/myZip.zip/InzipDir/InzipFile -destination C:/OtherDir -keep no -force yes
call zipjs.bat unZipItem -source C:/myDir/myZip.zip/InzipDir -destination C:/OtherDir
// adds content to a zip file
call zipjs.bat addToZip -source C:/some_file -destination C:/myDir/myZip.zip/InzipDir -keep no
call zipjs.bat addToZip -source C:/some_file -destination C:/myDir/myZip.zip
Algunos problemas conocidos durante la compresión:
- Si no hay suficiente espacio en la unidad del sistema (generalmente C :), el script puede producir varios errores, la mayoría de las veces el script se detiene. Esto se debe a Shell. La aplicación utiliza activamente la carpeta % TEMP% para comprimir / descomprimir los datos.
- El objeto Shell.Application no puede manejar las carpetas y los archivos que contienen símbolos Unicode en sus nombres.
- El tamaño máximo admitido de los archivos zip producidos es de alrededor de 8 gb en Vista y superior y alrededor de 2 gb en XP / 2003
El script detecta si aparece un mensaje de error y detiene la ejecución e informa por las posibles razones. En este momento no tengo forma de detectar el texto dentro de la ventana emergente y dar la razón exacta del error.
3. Makecab .
Comprimir un archivo es fácil:
makecab file.txt "file.cab"
.
Finalmente,
MaxCabinetSize
podría aumentarse.
La compresión de una carpeta requiere una directiva
DestinationDir de
uso (con rutas relativas) para cada (sub) directorio y los archivos dentro.
Aquí hay un script:
;@echo off
;;;;; rem start of the batch part ;;;;;
;
;for %%a in (/h /help -h -help) do (
; if /I "%~1" equ "%%~a" if "%~2" equ "" (
; echo compressing directory to cab file
; echo Usage:
; echo(
; echo %~nx0 "directory" "cabfile"
; echo(
; echo to uncompress use:
; echo EXPAND cabfile -F:* .
; echo(
; echo Example:
; echo(
; echo %~nx0 "c:/directory/logs" "logs"
; exit /b 0
; )
; )
;
; if "%~2" EQU "" (
; echo invalid arguments.For help use:
; echo %~nx0 /h
; exit /b 1
;)
;
; set "dir_to_cab=%~f1"
;
; set "path_to_dir=%~pn1"
; set "dir_name=%~n1"
; set "drive_of_dir=%~d1"
; set "cab_file=%~2"
;
; if not exist %dir_to_cab%/ (
; echo no valid directory passed
; exit /b 1
;)
;
;break>"%tmp%/makecab.dir.ddf"
;
;setlocal enableDelayedExpansion
;for /d /r "%dir_to_cab%" %%a in (*) do (
;
; set "_dir=%%~pna"
; set "destdir=%dir_name%!_dir:%path_to_dir%=!"
; (echo(.Set DestinationDir=!destdir!>>"%tmp%/makecab.dir.ddf")
; for %%# in ("%%a/*") do (
; (echo("%%~f#" /inf=no>>"%tmp%/makecab.dir.ddf")
; )
;)
;(echo(.Set DestinationDir=!dir_name!>>"%tmp%/makecab.dir.ddf")
; for %%# in ("%~f1/*") do (
;
; (echo("%%~f#" /inf=no>>"%tmp%/makecab.dir.ddf")
; )
;makecab /F "%~f0" /f "%tmp%/makecab.dir.ddf" /d DiskDirectory1=%cd% /d CabinetNameTemplate=%cab_file%.cab
;rem del /q /f "%tmp%/makecab.dir.ddf"
;exit /b %errorlevel%
;;
;;;; rem end of the batch part ;;;;;
;;;; directives part ;;;;;
;;
.New Cabinet
.set GenerateInf=OFF
.Set Cabinet=ON
.Set Compress=ON
.Set UniqueFiles=ON
.Set MaxDiskSize=1215751680;
.set RptFileName=nul
.set InfFileName=nul
.set MaxErrors=1
;;
;;;; end of directives part ;;;;;
Para descompresión
EXPAND cabfile -F:* .
se puede usar para extracción en Unix
cabextract
o
7zip
.
4. .NET y GZipStream
Preferí un Jscript.net ya que permite una hibridación ordenada con .bat (sin salida tóxica y sin archivos temporales) .Jscript no permite pasar una referencia de objeto a una función, por lo que la única forma que encontré para hacerlo funcionar es leer / escribir archivos byte a byte (así que supongo que no es la forma más rápida, ¿cómo se puede hacer la lectura / escritura almacenada?) De nuevo, solo se puede usar con archivos individuales.
@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal
for /f "tokens=* delims=" %%v in (''dir /b /s /a:-d /o:-n "%SystemRoot%/Microsoft.NET/Framework/*jsc.exe"'') do (
set "jsc=%%v"
)
if not exist "%~n0.exe" (
"%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)
%~n0.exe %*
endlocal & exit /b %errorlevel%
*/
import System;
import System.Collections.Generic;
import System.IO;
import System.IO.Compression;
function CompressFile(source,destination){
var sourceFile=File.OpenRead(source);
var destinationFile=File.Create(destination);
var output = new GZipStream(destinationFile,CompressionMode.Compress);
Console.WriteLine("Compressing {0} to {1}.", sourceFile.Name,destinationFile.Name, false);
var byteR = sourceFile.ReadByte();
while(byteR !=- 1){
output.WriteByte(byteR);
byteR = sourceFile.ReadByte();
}
sourceFile.Close();
output.Flush();
output.Close();
destinationFile.Close();
}
function UncompressFile(source,destination){
var sourceFile=File.OpenRead(source);
var destinationFile=File.Create(destination);
var input = new GZipStream(sourceFile,
CompressionMode.Decompress, false);
Console.WriteLine("Decompressing {0} to {1}.", sourceFile.Name,
destinationFile.Name);
var byteR=input.ReadByte();
while(byteR !== -1){
destinationFile.WriteByte(byteR);
byteR=input.ReadByte();
}
destinationFile.Close();
input.Close();
}
var arguments:String[] = Environment.GetCommandLineArgs();
function printHelp(){
Console.WriteLine("Compress and uncompress gzip files:");
Console.WriteLine("Compress:");
Console.WriteLine(arguments[0]+" -c source destination");
Console.WriteLine("Uncompress:");
Console.WriteLine(arguments[0]+" -u source destination");
}
if (arguments.length!=4){
Console.WriteLine("Wrong arguments");
printHelp();
Environment.Exit(1);
}
switch (arguments[1]){
case "-c":
CompressFile(arguments[2],arguments[3]);
break;
case "-u":
UncompressFile(arguments[2],arguments[3]);
break;
default:
Console.WriteLine("Wrong arguments");
printHelp();
Environment.Exit(1);
}
soluciones asombrosas!
La solución makecab tiene algunos problemas, así que aquí hay una versión fija que resuelve el problema cuando se usan directorios con espacios en blanco.
;@echo off
;;;;; rem start of the batch part ;;;;;
;
;for %%a in (/h /help -h -help) do (
; if /I "%~1" equ "%%~a" if "%~2" equ "" (
; echo compressing directory to cab file
; echo Usage:
; echo(
; echo %~nx0 "directory" "cabfile"
; echo(
; echo to uncompress use:
; echo EXPAND cabfile -F:* .
; echo(
; echo Example:
; echo(
; echo %~nx0 "c:/directory/logs" "logs"
; exit /b 0
; )
; )
;
; if "%~2" EQU "" (
; echo invalid arguments.For help use:
; echo %~nx0 /h
; exit /b 1
;)
;
; set "dir_to_cab=%~f1"
;
; set "path_to_dir=%~pn1"
; set "dir_name=%~n1"
; set "drive_of_dir=%~d1"
; set "cab_file=%~2"
;
; if not exist "%dir_to_cab%/" (
; echo no valid directory passed
; exit /b 1
;)
;
;break>"%tmp%/makecab.dir.ddf"
;
;setlocal enableDelayedExpansion
;for /d /r "%dir_to_cab%" %%a in (*) do (
;
; set "_dir=%%~pna"
; set "destdir=%dir_name%!_dir:%path_to_dir%=!"
; (echo(.Set DestinationDir=!destdir!>>"%tmp%/makecab.dir.ddf")
; for %%# in ("%%a/*") do (
; (echo("%%~f#" /inf=no>>"%tmp%/makecab.dir.ddf")
; )
;)
;(echo(.Set DestinationDir=!dir_name!>>"%tmp%/makecab.dir.ddf")
; for %%# in ("%~f1/*") do (
;
; (echo("%%~f#" /inf=no>>"%tmp%/makecab.dir.ddf")
; )
;makecab /F "%~f0" /f "%tmp%/makecab.dir.ddf" /d DiskDirectory1="%cd%" /d CabinetNameTemplate=%cab_file%.cab
;rem del /q /f "%tmp%/makecab.dir.ddf"
;exit /b %errorlevel%
;;
;;;; rem end of the batch part ;;;;;
;;;; directives part ;;;;;
;;
.New Cabinet
.set GenerateInf=OFF
.Set Cabinet=ON
.Set Compress=ON
.Set UniqueFiles=ON
.Set MaxDiskSize=1215751680;
.set RptFileName=nul
.set InfFileName=nul
.set MaxErrors=1
;;
;;;; end of directives part ;;;;;
CAB.bat
[entrada] carpeta o archivo: paquete |
.cab o. ?? _: desempaquetar |
ninguno: empacar una subcarpeta de
files
También agregará una entrada
CAB
para hacer clic con el botón derecho en el menú Enviar a para facilitar el manejo
Dado que esta realiza ambas tareas a la perfección, debería preferirse a la fea makecab: ¿por qué usar un script híbrido si de todos modos escribe en un archivo temporal?
@echo off &echo. &set "ext=%~x1" &title CAB [%1] &rem input file or folder / ''files'' folder / unpacks .cab .??_
if "_%1"=="_" if not exist "%~dp0files" echo CAB: No input and no ''files'' directory to pack &goto :Exit "do nothing"
if "_%1"=="_" if exist "%~dp0files" call :CabDir "%~dp0files" &goto :Exit "input = none, use ''files'' directory -pack"
for /f "tokens=1 delims=r-" %%I in ("%~a1") do if "_%%I"=="_d" call :CabDir "%~f1" &goto :Exit "input = dir -pack"
if not "_%~x1"=="_.cab" if not "_%ext:~-1%"=="__" call :CabFile "%~f1" &goto :Exit "input = file -pack"
call :CabExtract "%~f1" &goto :Exit "input = .cab or .??_ -unpack"
:Exit AveYo: script will add a CAB entry to right-click -- SendTo menu
if not exist "%APPDATA%/Microsoft/Windows/SendTo/CAB.bat" copy /y "%~f0" "%APPDATA%/Microsoft/Windows/SendTo/CAB.bat" >nul 2>nul
ping -n 6 localhost >nul &title cmd.exe &exit /b
:CabExtract %1:[.cab or .xx_]
echo %1 &pushd "%~dp1" &mkdir "%~n1" >nul 2>nul &expand -R "%~1" -F:* "%~n1" &popd &goto :eof
:CabFile %1:[filename]
echo %1 &pushd "%~dp1" &makecab /D CompressionType=LZX /D CompressionLevel=7 /D CompressionMemory=21 "%~nx1" "%~n1.cab" &goto :eof
:CabDir %1:[directory]
dir /a:-D/b/s "%~1"
set "ddf="%temp%/ddf""
echo/.New Cabinet>%ddf%
echo/.set Cabinet=ON>>%ddf%
echo/.set CabinetFileCountThreshold=0;>>%ddf%
echo/.set Compress=ON>>%ddf%
echo/.set CompressionType=LZX>>%ddf%
echo/.set CompressionLevel=7;>>%ddf%
echo/.set CompressionMemory=21;>>%ddf%
echo/.set FolderFileCountThreshold=0;>>%ddf%
echo/.set FolderSizeThreshold=0;>>%ddf%
echo/.set GenerateInf=OFF>>%ddf%
echo/.set InfFileName=nul>>%ddf%
echo/.set MaxCabinetSize=0;>>%ddf%
echo/.set MaxDiskFileCount=0;>>%ddf%
echo/.set MaxDiskSize=0;>>%ddf%
echo/.set MaxErrors=1;>>%ddf%
echo/.set RptFileName=nul>>%ddf%
echo/.set UniqueFiles=ON>>%ddf%
setlocal enabledelayedexpansion
pushd "%~dp1"
for /f "tokens=* delims=" %%D in (''dir /a:-D/b/s "%~1"'') do (
set "DestinationDir=%%~dpD" &set "DestinationDir=!DestinationDir:%~1=!" &set "DestinationDir=!DestinationDir:~0,-1!"
echo/.Set DestinationDir=!DestinationDir!;>>%ddf%
echo/"%%~fD" /inf=no;>>%ddf%
)
makecab /F %ddf% /D DiskDirectory1="" /D CabinetNameTemplate=%~nx1.cab &endlocal &popd &del /q /f %ddf% &goto :eof