我想从一个jar文件中复制一个文件。被复制的文件将要被复制到工作目录之外。我尝试了多种方法,但所有的方法都会导致复制出来的文件大小为0字节。
编辑:我希望通过程序自动实现文件复制,而不是手动操作。
我想从一个jar文件中复制一个文件。被复制的文件将要被复制到工作目录之外。我尝试了多种方法,但所有的方法都会导致复制出来的文件大小为0字节。
编辑:我希望通过程序自动实现文件复制,而不是手动操作。
首先我想说之前发布的一些答案完全正确,但我想给出我的答案,因为有时我们不能在GPL下使用开源库,或者因为我们太懒了不想下载jar文件XD,或者其他任何原因,这里是一个独立的解决方案。
下面的函数将复制与Jar文件相邻的资源:
/**
* Export a resource embedded into a Jar file to the local file path.
*
* @param resourceName ie.: "/SmartLibrary.dll"
* @return The path to the exported resource
* @throws Exception
*/
static public String ExportResource(String resourceName) throws Exception {
InputStream stream = null;
OutputStream resStreamOut = null;
String jarFolder;
try {
stream = ExecutingClass.class.getResourceAsStream(resourceName);//note that each / is a directory down in the "jar tree" been the jar the root of the tree
if(stream == null) {
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
}
int readBytes;
byte[] buffer = new byte[4096];
jarFolder = new File(ExecutingClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath().replace('\\', '/');
resStreamOut = new FileOutputStream(jarFolder + resourceName);
while ((readBytes = stream.read(buffer)) > 0) {
resStreamOut.write(buffer, 0, readBytes);
}
} catch (Exception ex) {
throw ex;
} finally {
stream.close();
resStreamOut.close();
}
return jarFolder + resourceName;
}
将ExecutingClass更改为您的类的名称,然后像这样调用:
String fullPath = ExportResource("/myresource.ext");
正如GOXR3PLUS所回答并被Andy Thomas指出,您可以通过以下方式实现:
Files.copy( InputStream in, Path target, CopyOption... options)
详见GOXR3PLUS 的回答了解更多细节
Files.copy(InputStream in, Path target, CopyOption... options)
来替换掉一些代码,正如下面的一个较新的答案所指出的那样。 - Andy Thomas鉴于您对0字节文件的评论,我认为您正在尝试以编程方式执行此操作,并且根据您的标签,您是在Java中执行此操作。如果是这样,那么只需使用Class.getResource()获取指向JAR文件中该文件的URL,然后使用Apache Commons IO FileUtils.copyURLToFile()将其复制到文件系统中。例如:
URL inputUrl = getClass().getResource("/absolute/path/of/source/in/jar/file");
File dest = new File("/path/to/destination/file");
FileUtils.copyURLToFile(inputUrl, dest);
很可能,你现有的代码问题在于你(正确地)使用了缓冲输出流来写入文件,但是(错误地)未关闭它。
哦,而且你应该编辑你的问题以澄清你想要以什么方式进行此操作(程序化,非程序化,语言,...)
使用 Java 7+ 更快的方法,以及获取当前目录的代码:
/**
* Copy a file from source to destination.
*
* @param source
* the source
* @param destination
* the destination
* @return True if succeeded , False if not
*/
public static boolean copy(InputStream source , String destination) {
boolean succeess = true;
System.out.println("Copying ->" + source + "\n\tto ->" + destination);
try {
Files.copy(source, Paths.get(destination), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
logger.log(Level.WARNING, "", ex);
succeess = false;
}
return succeess;
}
测试它(icon.png 是应用程序包图像中的一张图片):
copy(getClass().getResourceAsStream("/image/icon.png"),getBasePathForClass(Main.class)+"icon.png");
关于这行代码(getBasePathForClass(Main.class)
):->请查看我在此处添加的答案 :) -> 在Java中获取当前工作目录。Java 8(实际上FileSystem自1.7就有了)带有一些很酷的新类/方法来处理这个问题。正如有人已经提到的,JAR基本上是ZIP文件,你可以使用
final URI jarFileUril = URI.create("jar:file:" + file.toURI().getPath());
final FileSystem fs = FileSystems.newFileSystem(jarFileUri, env);
fs.getPath("filename");
然后您可以使用 Files 类
try (final Stream<Path> sources = Files.walk(from)) {
sources.forEach(src -> {
final Path dest = to.resolve(from.relativize(src).toString());
try {
if (Files.isDirectory(from)) {
if (Files.notExists(to)) {
log.trace("Creating directory {}", to);
Files.createDirectories(to);
}
} else {
log.trace("Extracting file {} to {}", from, to);
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException("Failed to unzip file.", e);
}
});
}
注意:我尝试解压JAR文件进行测试。
"jar:file:"+file.toURI().getPath()
looks somewhat nonsensical. Why not simply "jar:"+file.toURI()
… by the way, it’s toURI()
rather than toUri()
- HolgergetPath()
方法来移除模式,然后又添加了一个包含相同模式的字符串。我发布了一个替代方案,两者都被移除,因此结果将是 jar:file:/foo/bar
,与你的变体完全相同。如果你不信,可以试试。 - Holger强大的解决方案:
public static void copyResource(String res, String dest, Class c) throws IOException {
InputStream src = c.getResourceAsStream(res);
Files.copy(src, Paths.get(dest), StandardCopyOption.REPLACE_EXISTING);
}
File tempFileGdalZip = File.createTempFile("temp_gdal", ".zip");
copyResource("/gdal.zip", tempFileGdalZip.getAbsolutePath(), this.getClass());
dest
必须是文件名或文件路径。 - Udo// assuming you already have an InputStream to the jar file..
JarInputStream jis = new JarInputStream( is );
// get the first entry
JarEntry entry = jis.getNextEntry();
// we will loop through all the entries in the jar file
while ( entry != null ) {
// test the entry.getName() against whatever you are looking for, etc
if ( matches ) {
// read from the JarInputStream until the read method returns -1
// ...
// do what ever you want with the read output
// ...
// if you only care about one file, break here
}
// get the next entry
entry = jis.getNextEntry();
}
jis.close();
要从您的jar文件中复制文件到外部,您需要使用以下方法:
getResourceAsStream()
}}获取指向jar文件内部文件的InputStream
FileOutputStream
打开目标文件示例代码还包含一个变量,用于不替换现有值:
public File saveResource(String name) throws IOException {
return saveResource(name, true);
}
public File saveResource(String name, boolean replace) throws IOException {
return saveResource(new File("."), name, replace)
}
public File saveResource(File outputDirectory, String name) throws IOException {
return saveResource(outputDirectory, name, true);
}
public File saveResource(File outputDirectory, String name, boolean replace)
throws IOException {
File out = new File(outputDirectory, name);
if (!replace && out.exists())
return out;
// Step 1:
InputStream resource = this.getClass().getResourceAsStream(name);
if (resource == null)
throw new FileNotFoundException(name + " (resource not found)");
// Step 2 and automatic step 4
try(InputStream in = resource;
OutputStream writer = new BufferedOutputStream(
new FileOutputStream(out))) {
// Step 3
byte[] buffer = new byte[1024 * 4];
int length;
while((length = in.read(buffer)) >= 0) {
writer.write(buffer, 0, length);
}
}
return out;
}
${JAVA_HOME}/bin/jar -cvf /path/to.jar
Foo.jar
重命名为Foo.zip
,然后正常解压缩即可。 - Jeffrey