从jar文件复制目录

我最近开发了一个应用程序并创建了jar文件。

我的一个类创建了一个输出目录,用它的资源填充文件。

我的代码是这样的:

// Copy files from dir "template" in this class resource to output. private void createOutput(File output) throws IOException { File template = new File(FileHelper.URL2Path(getClass().getResource("template"))); FileHelper.copyDirectory(template, output); } 

不幸的是,这不起作用。

没有运气我尝试了以下内容:

  • 使用Streams来解决其他类中的类似内容,但它不适用于dirs。 代码类似于http://www.exampledepot.com/egs/java.io/CopyFile.html

  • 使用new File(getClass().getResource("template").toUri())创建文件模板new File(getClass().getResource("template").toUri())

在写这篇文章的时候,我正在思考而不是在资源路径中有一个模板目录,而是有一个zip文件。 这样做我可以将文件作为inputStream并将其解压缩到我需要的位置。 但我不确定这是不是正确的方法。

我认为你使用zip文件的方法是有道理的。 大概你会做一个getResourceAsStream来获取zip的内部,这在逻辑上看起来就像一个目录树。

骨架方法:

 InputStream is = getClass().getResourceAsStream("my_embedded_file.zip"); ZipInputStream zis = new ZipInputStream(is); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { // do something with the entry - for example, extract the data } 

谢谢你的解决方案! 对于其他人,以下内容不使用辅助类(StringUtils除外)

/ 我为此解决方案添加了额外的信息,检查代码的结尾,Zegor V /

 public class FileUtils { public static boolean copyFile(final File toCopy, final File destFile) { try { return FileUtils.copyStream(new FileInputStream(toCopy), new FileOutputStream(destFile)); } catch (final FileNotFoundException e) { e.printStackTrace(); } return false; } private static boolean copyFilesRecusively(final File toCopy, final File destDir) { assert destDir.isDirectory(); if (!toCopy.isDirectory()) { return FileUtils.copyFile(toCopy, new File(destDir, toCopy.getName())); } else { final File newDestDir = new File(destDir, toCopy.getName()); if (!newDestDir.exists() && !newDestDir.mkdir()) { return false; } for (final File child : toCopy.listFiles()) { if (!FileUtils.copyFilesRecusively(child, newDestDir)) { return false; } } } return true; } public static boolean copyJarResourcesRecursively(final File destDir, final JarURLConnection jarConnection) throws IOException { final JarFile jarFile = jarConnection.getJarFile(); for (final Enumeration e = jarFile.entries(); e.hasMoreElements();) { final JarEntry entry = e.nextElement(); if (entry.getName().startsWith(jarConnection.getEntryName())) { final String filename = StringUtils.removeStart(entry.getName(), // jarConnection.getEntryName()); final File f = new File(destDir, filename); if (!entry.isDirectory()) { final InputStream entryInputStream = jarFile.getInputStream(entry); if(!FileUtils.copyStream(entryInputStream, f)){ return false; } entryInputStream.close(); } else { if (!FileUtils.ensureDirectoryExists(f)) { throw new IOException("Could not create directory: " + f.getAbsolutePath()); } } } } return true; } public static boolean copyResourcesRecursively( // final URL originUrl, final File destination) { try { final URLConnection urlConnection = originUrl.openConnection(); if (urlConnection instanceof JarURLConnection) { return FileUtils.copyJarResourcesRecursively(destination, (JarURLConnection) urlConnection); } else { return FileUtils.copyFilesRecusively(new File(originUrl.getPath()), destination); } } catch (final IOException e) { e.printStackTrace(); } return false; } private static boolean copyStream(final InputStream is, final File f) { try { return FileUtils.copyStream(is, new FileOutputStream(f)); } catch (final FileNotFoundException e) { e.printStackTrace(); } return false; } private static boolean copyStream(final InputStream is, final OutputStream os) { try { final byte[] buf = new byte[1024]; int len = 0; while ((len = is.read(buf)) > 0) { os.write(buf, 0, len); } is.close(); os.close(); return true; } catch (final IOException e) { e.printStackTrace(); } return false; } private static boolean ensureDirectoryExists(final File f) { return f.exists() || f.mkdir(); } } 

它仅使用Apache Software Foundation中的一个外部库,但所使用的function仅限于:

  public static String removeStart(String str, String remove) { if (isEmpty(str) || isEmpty(remove)) { return str; } if (str.startsWith(remove)){ return str.substring(remove.length()); } return str; } public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } 

我的知识仅限于Apache许可证,但您可以在没有库的代码中使用此方法。 但是,如果有的话,我不对许可证问题负责。

使用Java7 +可以通过创建FileSystem然后使用walkFileTree递归复制文件来实现。

 public void copyFromJar(String source, final Path target) throws URISyntaxException, IOException { URI resource = getClass().getResource("").toURI(); FileSystem fileSystem = FileSystems.newFileSystem( resource, Collections.emptyMap() ); final Path jarPath = fileSystem.getPath(source); Files.walkFileTree(jarPath, new SimpleFileVisitor() { private Path currentTarget; @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { currentTarget = target.resolve(jarPath.relativize(dir).toString()); Files.createDirectories(currentTarget); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, target.resolve(jarPath.relativize(file).toString()), REPLACE_EXISTING); return FileVisitResult.CONTINUE; } }); } 

该方法可以这样使用:

 copyFromJar("/path/to/the/template/in/jar", Paths.get("/tmp/from-jar")) 

我讨厌使用之前发布的ZIP文件方法的想法,所以我想出了以下内容。

 public void copyResourcesRecursively(URL originUrl, File destination) throws Exception { URLConnection urlConnection = originUrl.openConnection(); if (urlConnection instanceof JarURLConnection) { copyJarResourcesRecursively(destination, (JarURLConnection) urlConnection); } else if (urlConnection instanceof FileURLConnection) { FileUtils.copyFilesRecursively(new File(originUrl.getPath()), destination); } else { throw new Exception("URLConnection[" + urlConnection.getClass().getSimpleName() + "] is not a recognized/implemented connection type."); } } public void copyJarResourcesRecursively(File destination, JarURLConnection jarConnection ) throws IOException { JarFile jarFile = jarConnection.getJarFile(); for (JarEntry entry : CollectionUtils.iterable(jarFile.entries())) { if (entry.getName().startsWith(jarConnection.getEntryName())) { String fileName = StringUtils.removeStart(entry.getName(), jarConnection.getEntryName()); if (!entry.isDirectory()) { InputStream entryInputStream = null; try { entryInputStream = jarFile.getInputStream(entry); FileUtils.copyStream(entryInputStream, new File(destination, fileName)); } finally { FileUtils.safeClose(entryInputStream); } } else { FileUtils.ensureDirectoryExists(new File(destination, fileName)); } } } } 

示例Useage(将类路径资源“config”中的所有文件复制到“$ {homeDirectory} / config”:

 File configHome = new File(homeDirectory, "config/"); //noinspection ResultOfMethodCallIgnored configHome.mkdirs(); copyResourcesRecursively(super.getClass().getResource("/config"), configHome); 

这应该适用于从平面文件和Jar文件复制。

注意:上面的代码使用了一些自定义实用程序类(FileUtils,CollectionUtils)以及一些来自Apache commons-lang(StringUtils)的代码,但是这些函数应该非常明显地命名。

我不确定FileHelper是什么或者做什么,但是你无法直接从JAR复制文件(或目录)。 正如您所提到的那样使用InputStream是正确的方法(来自jar或zip):

 InputStream is = getClass().getResourceAsStream("file_in_jar"); OutputStream os = new FileOutputStream("dest_file"); byte[] buffer = new byte[4096]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } os.close(); is.close(); 

您需要为每个文件执行上述操作(当然适当地处理exception)。 您可能会或可能不会(根据您的部署配置)将有问题的jar文件读取为JarFile (例如,如果部署为非扩展Web应用程序的一部分,则可能无法将其作为实际文件提供)。 如果您可以阅读它,您应该能够遍历JarEntry实例列表,从而重新构建您的目录结构; 否则你可能需要将它存储在别处(例如在text或xml资源中)

您可能需要查看Commons IO库 – 它提供了许多常用的流/文件function,包括复制。

这是tess4j项目的工作版本:

  /** * This method will copy resources from the jar file of the current thread and extract it to the destination folder. * * @param jarConnection * @param destDir * @throws IOException */ public void copyJarResourceToFolder(JarURLConnection jarConnection, File destDir) { try { JarFile jarFile = jarConnection.getJarFile(); /** * Iterate all entries in the jar file. */ for (Enumeration e = jarFile.entries(); e.hasMoreElements();) { JarEntry jarEntry = e.nextElement(); String jarEntryName = jarEntry.getName(); String jarConnectionEntryName = jarConnection.getEntryName(); /** * Extract files only if they match the path. */ if (jarEntryName.startsWith(jarConnectionEntryName)) { String filename = jarEntryName.startsWith(jarConnectionEntryName) ? jarEntryName.substring(jarConnectionEntryName.length()) : jarEntryName; File currentFile = new File(destDir, filename); if (jarEntry.isDirectory()) { currentFile.mkdirs(); } else { InputStream is = jarFile.getInputStream(jarEntry); OutputStream out = FileUtils.openOutputStream(currentFile); IOUtils.copy(is, out); is.close(); out.close(); } } } } catch (IOException e) { // TODO add logger e.printStackTrace(); } } 

您可以使用ClassLoader获取资源的流 。 获得InputStream后,您可以读取并将流的内容写入OutputStream。

在您的情况下,您需要创建多个OutputStream实例,每个实例对应要复制到目标的每个文件。 当然,这要求您事先知道文件名。

对于此任务,最好使用getResourceAsStream,而不是getResource或getResources()。

lpiepiora的答案是正确的! 但是有一个小问题,来源,应该是一个jar url。 当源路径是文件系统的路径时,上述代码将无法正常工作。 要解决这个问题,你应该使用ReferencePath,代码,你可以从以下链接获得: 通过FileSystem对象从文件系统读取 copyFromJar的新代码应该如下:

 public class ResourcesUtils { public static void copyFromJar(final String sourcePath, final Path target) throws URISyntaxException, IOException { final PathReference pathReference = PathReference.getPath(new URI(sourcePath)); final Path jarPath = pathReference.getPath(); Files.walkFileTree(jarPath, new SimpleFileVisitor() { private Path currentTarget; @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { currentTarget = target.resolve(jarPath.relativize(dir) .toString()); Files.createDirectories(currentTarget); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { Files.copy(file, target.resolve(jarPath.relativize(file) .toString()), StandardCopyOption.REPLACE_EXISTING); return FileVisitResult.CONTINUE; } }); } public static void main(final String[] args) throws MalformedURLException, URISyntaxException, IOException { final String sourcePath = "jar:file:/c:/temp/example.jar!/src/main/resources"; ResourcesUtils.copyFromJar(sourcePath, Paths.get("c:/temp/resources")); } 

我最近遇到了类似的问题。 我试图从java资源中提取文件夹。 所以我用Spring PathMatchingResourcePatternResolver解决了这个问题。

此代码从指定的资源获取所有文件和目录:

  ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceFolder + "/**"); 

这是将所有文件和目录从资源复制到磁盘路径的类。

 public class ResourceExtractor { public static final Logger logger = Logger.getLogger(ResourceExtractor.class); public void extract(String resourceFolder, String destinationFolder){ try { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceFolder + "/**"); URI inJarUri = new DefaultResourceLoader().getResource("classpath:" + resourceFolder).getURI(); for (Resource resource : resources){ String relativePath = resource .getURI() .getRawSchemeSpecificPart() .replace(inJarUri.getRawSchemeSpecificPart(), ""); if (relativePath.isEmpty()){ continue; } if (relativePath.endsWith("/") || relativePath.endsWith("\\")) { File dirFile = new File(destinationFolder + relativePath); if (!dirFile.exists()) { dirFile.mkdir(); } } else{ copyResourceToFilePath(resource, destinationFolder + relativePath); } } } catch (IOException e){ logger.debug("Extraction failed!", e ); } } private void copyResourceToFilePath(Resource resource, String filePath) throws IOException{ InputStream resourceInputStream = resource.getInputStream(); File file = new File(filePath); if (!file.exists()) { FileUtils.copyInputStreamToFile(resourceInputStream, file); } } 

}