这篇文章演示了Groovy脚本,用于解压缩7-Zip存档格式的文件。 这篇文章的两个主要目的是演示使用Groovy解压缩7-Zip文件和方便的7-Zip-JBinding ,并说明并演示Groovy作为脚本语言的一些关键特性。
7-Zip页面将7-Zip描述为“具有高压缩率的文件存档器”。 该页面还添加了“ 7-Zip是开源软件。 大多数源代码均已获得GNU LGPL许可。” 该站点上提供了更多许可信息以及7z格式的信息 (“ LZMA是默认的7z格式的通用压缩方法”)。
7-Zip页面将其描述为“ 7-Zip C ++库的Java包装程序”,“允许使用非常快速的本机库直接通过JNI从Java中提取许多存档格式。” 7z格式基于“ LZMA和LZMA2压缩”。 尽管有可用的LZMA SDK ,但是在使用Java处理7-Zip文件时,使用开源( SourceForge ) 7-Zip-JBinding项目更加容易。
StackOverflow线程在Java中以.7z扩展名解压缩文件中提供了一个将Java与7-Zip-JBinding一起使用来解压缩7z文件的好例子。 Dark Knight的响应指示如何将Java与7-Zip-JBinding一起使用来解压缩7z文件。 在本文中,我将Dark Knight的Java代码改编为Groovy脚本。
为了演示适用于解压缩7z文件的Groovy代码,我首先需要一个可以提取内容的7z文件。 下一个屏幕快照系列向我展示了如何使用笔记本电脑上安装的Windows 7-Zip将Guava Downloads页面下的六个PDF压缩为一个名为Guava.7z
的单个7z文件。
坐在文件夹中的六个番石榴PDF
选择的内容和右键单击菜单以压缩为7z格式
Guava.7z压缩存档文件已创建
有了7z文件后,我现在转到改编的Groovy脚本,该脚本将提取此Guava.7z
文件的内容。 如前所述,该Groovy脚本是Dark Knight在StackOverflow线程上提供的Java代码的改编。
解压缩
//
// This Groovy script is adapted from Java code provided at
// http://stackoverflow.com/a/19403933
import static java.lang.System.err as error
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.RandomAccessFile
import java.util.Arrays
import net.sf.sevenzipjbinding.ExtractOperationResult
import net.sf.sevenzipjbinding.ISequentialOutStream
import net.sf.sevenzipjbinding.ISevenZipInArchive
import net.sf.sevenzipjbinding.SevenZip
import net.sf.sevenzipjbinding.SevenZipException
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream
import net.sf.sevenzipjbinding.simple.ISimpleInArchive
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem
if (args.length < 1)
{
println "USAGE: unzip7z.groovy .7z\n"
System.exit(-1)
}
def fileToUnzip = args[0]
try
{
RandomAccessFile randomAccessFile = new RandomAccessFile(fileToUnzip, "r")
ISevenZipInArchive inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface()
println "${'Hash'.center(10)}|${'Size'.center(12)}|${'Filename'.center(10)}"
println "${'-'.multiply(10)}+${'-'.multiply(12)}+${'-'.multiply(10)}"
simpleInArchive.getArchiveItems().each
{ item ->
final int[] hash = new int[1]
if (!item.isFolder())
{
final long[] sizeArray = new long[1]
ExtractOperationResult result = item.extractSlow(
new ISequentialOutStream()
{
public int write(byte[] data) throws SevenZipException
{
//Write to file
try
{
File file = new File(item.getPath())
file.getParentFile()?.mkdirs()
FileOutputStream fos = new FileOutputStream(file)
fos.write(data)
fos.close()
}
catch (Exception e)
{
printExceptionStackTrace("Unable to write file", e)
}
hash[0] ^= Arrays.hashCode(data) // Consume data
sizeArray[0] += data.length
return data.length // Return amount of consumed data
}
})
if (result == ExtractOperationResult.OK)
{
println(String.format("%9X | %10s | %s",
hash[0], sizeArray[0], item.getPath()))
}
else
{
error.println("Error extracting item: " + result)
}
}
}
}
catch (Exception e)
{
printExceptionStackTrace("Error occurs", e)
System.exit(1)
}
finally
{
if (inArchive != null)
{
try
{
inArchive.close()
}
catch (SevenZipException e)
{
printExceptionStackTrace("Error closing archive", e)
}
}
if (randomAccessFile != null)
{
try
{
randomAccessFile.close()
}
catch (IOException e)
{
printExceptionStackTrace("Error closing file", e)
}
}
}
/**
* Prints the stack trace of the provided exception to standard error without
* Groovy meta data trace elements.
*
* @param contextMessage String message to precede stack trace and provide context.
* @param exceptionToBePrinted Exception whose Groovy-less stack trace should
* be printed to standard error.
* @return Exception derived from the provided Exception but without Groovy
* meta data calls.
*/
def Exception printExceptionStackTrace(
final String contextMessage, final Exception exceptionToBePrinted)
{
error.print "${contextMessage}: ${org.codehaus.groovy.runtime.StackTraceUtils.sanitize(exceptionToBePrinted).printStackTrace()}"
}
在将Java代码改编为上面所示的Groovy脚本时,我保留了大部分异常处理。 尽管Groovy允许忽略异常(无论是选中还是未选中),但在这种情况下,我还是希望保持这种处理方式,以确保正确关闭资源并将适当的错误消息呈现给脚本用户。
我所做的更改之一是使所有与错误相关的输出都打印为标准错误,而不是标准输出。 这需要一些更改。 首先,我使用Groovy的功能来重命名静态导入的内容 (请参阅我的相关文章Groovier Static Imports ),将“ java.lang.System.err”引用为“ error”,这样我就可以简单地将“ error”用作句柄该脚本,而不需要使用“ System.err”来访问标准错误以进行输出。
因为Throwable.printStackTrace()已经写入标准错误而不是标准输出,所以我只是直接使用它。 但是,我将对它的调用放在一个新方法中,该方法首先运行StackTraceUtils.sanitize(Throwable),以从堆栈跟踪中删除与Groovy的运行时动态功能关联的特定于Groovy的调用。
作为使它成为Groovier的一部分,对该脚本进行了其他一些小的更改。 我对存档文件中的项目而不是Java for
循环使用Groovy的迭代,在语句末尾删除了分号,对Groovy的String GDK扩展使用了更可控的输出报告 [自动将标题居中并通过给定的字符乘以适当的[需要存在的次数],并利用Groovy隐式包含args来添加检查以确保将提取文件提供给脚本。
准备好要提取的文件并准备好Groovy脚本后,就该提取我在本文前面演示的生成的Guava.7z
文件的内容了。 以下命令将运行脚本并将相应的7-Zip-JBinding JAR文件放置在类路径上。
groovy -classpath "C:/sevenzipjbinding/lib/sevenzipjbinding.jar;C:/sevenzipjbinding/lib/sevenzipjbinding-Windows-x86.jar" unzip7z.groovy C:\Users\Dustin\Downloads\Guava\Guava.7z
在显示针对指示的Guava.7z文件运行上述脚本的输出之前,请务必注意如果本机操作系统特定的7-Zip-JBinding JAR(sevenzipjbinding-Windows-x86.jar笔记本电脑的情况下)不包含在脚本的类路径中。
如最后一个屏幕快照所示,忽略在类路径中包含本机JAR会导致错误消息:“发生错误:java.lang.RuntimeException:SevenZipJBinding无法使用依赖于平台的JAR和默认临时目录的初始化来自动初始化。 请确保正确的'sevenzipjbinding-
.jar”文件位于类路径中,或考虑使用提供的初始化方法之一手动初始化SevenZipJBinding:'net.sf.sevenzipjbinding.SevenZip.init *()'”
尽管我只是在脚本的类路径中添加了C:/sevenzipjbinding/lib/sevenzipjbinding-Windows-x86.jar
以使其在此便携式计算机上运行,但功能更强大的脚本可能会检测到操作系统并将适当的JAR应用于该操作的类路径系统。 7-Zip-JBinding下载页面具有多个特定于平台的下载(包括特定于平台的JAR),例如sevenzipjbinding-4.65-1.06-rc-extr-only-Windows- amd64 .zip
, sevenzipjbinding-4.65-1.06-rc-extr-only- Mac-x86_64 .zip
, sevenzipjbinding-4.65-1.06-rc-extr-only- Mac-i386 .zip
和sevenzipjbinding-4.65-1.06-rc-extr-only- Linux-i386 .zip
一旦将本机7-Zip-JBinding JAR与核心sevenzipjbinding.jar JAR一起包括在类路径中,该脚本将运行得很漂亮,如下面的屏幕快照所示。
该脚本将7z文件的内容提取到与Groovy脚本相同的工作目录中。 进一步的增强将是修改脚本以接受将提取文件写入其中的目录,或者默认情况下可以将它们写入与7z存档文件相同的目录中。 使用Groovy的内置CLIBuilder支持还可以改善脚本。
在编写使用JVM和/或Java库和框架的脚本时,Groovy是我的首选语言。 写这篇文章主题的脚本再次提醒了我们这一点。
翻译自: https://www.javacodegeeks.com/2013/12/uncompressing-7-zip-files-with-groovy-and-7-zip-jbinding.html