从jar文件复制目录

Mac*_*rse 32 java jar getresource

我最近开发了一个应用程序并创建了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);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不起作用.

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

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

小智 16

谢谢你的解决方案!对于其他人,以下内容不使用辅助类(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<JarEntry> 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  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;
  }
Run Code Online (Sandbox Code Playgroud)

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


Vin*_*jip 11

我认为你使用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 
}
Run Code Online (Sandbox Code Playgroud)


lpi*_*ora 11

使用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.<String, String>emptyMap()
    );


    final Path jarPath = fileSystem.getPath(source);

    Files.walkFileTree(jarPath, new SimpleFileVisitor<Path>() {

        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()), StandardCopyOption.REPLACE_EXISTING);
            return FileVisitResult.CONTINUE;
        }

    });
}
Run Code Online (Sandbox Code Playgroud)

该方法可以这样使用:

copyFromJar("/path/to/the/template/in/jar", Paths.get("/tmp/from-jar"))
Run Code Online (Sandbox Code Playgroud)

  • 这个答案不起作用.我很想尝试使用(相对)新的nio API,但我似乎无法使其工作:java.lang.IllegalArgumentException:Path组件应为'/' (9认同)

niv*_*eth 6

我讨厌使用之前发布的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));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 你在用什么图书馆?(FileUtils,CollectionUtils,...) (2认同)

小智 6

lpiepiora 的回答,是正确的!但是有个小问题,源码,应该是jar Url。当源路径是文件系统的路径时,上面的代码将无法正常工作。为了解决这个问题,你应该使用ReferencePath,代码,你可以从以下链接中获取: Read from file system via FileSystem object 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<Path>() {

        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"));
}
Run Code Online (Sandbox Code Playgroud)