Java 从jar文件复制目录

Java 从jar文件复制目录,java,jar,getresource,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().ge

我最近开发了一个应用程序并创建了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);
}
copyFromJar("/path/to/the/template/in/jar", Paths.get("/tmp/from-jar"))
不幸的是,这不起作用

我尝试了以下方法,但运气不佳:

  • 使用流解决类似问题 在其他课程上,但它不起作用 用dirs。代码类似于

  • 使用
    新文件(getClass().getResource(“模板”).Tori())创建文件模板

在写这篇文章时,我考虑的是在资源路径中使用一个模板目录,而不是一个zip文件。通过这种方式,我可以将文件作为inputStream获取,并在需要的地方将其解压缩。但我不确定这是不是正确的方法。

您可以使用获取一个。一旦获得了InputStream,就可以读取流的内容并将其写入OutputStream

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


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

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

您需要为每个文件执行上述操作(当然,适当地处理异常)。您可能无法(取决于您的部署配置)将有问题的jar文件读取为(例如,如果将其部署为非扩展web应用程序的一部分,它可能无法作为实际文件使用)。若您可以阅读它,那个么您应该能够遍历JarEntry实例列表,从而重新构建目录结构;否则,您可能需要将其存储在其他位置(例如,在文本或xml资源中)


您可能想看看library—它提供了许多常用的流/文件功能,包括复制功能。

我认为您使用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 
}

我讨厌使用前面发布的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));
            }
        }
    }
}
示例用法(将所有文件从类路径资源“config”复制到“${homeDirectory}/config”:

这对于从平面文件和Jar文件复制都应该有效


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

感谢您的解决方案!对于其他的,下面的代码没有使用辅助类(StringUtils除外)

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


我对Apache许可证的了解有限,但您可以在代码中使用此方法,而无需使用库。但是,如果有许可证问题,我不负责。

使用Java7+这可以通过创建
文件系统
,然后使用
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;
        }

    });
}

以下是该项目的工作版本:

/**
*此方法将从当前线程的jar文件复制资源,并将其解压缩到目标文件夹。
* 
*@param-jar连接
*@param destDir
*@抛出异常
*/
public void copyJarResourceToFolder(JarURLConnection jarConnection,文件destDir){
试一试{
JarFile JarFile=jarConnection.getJarFile();
/**
*迭代jar文件中的所有条目。
*/
对于(枚举e=jarFile.entries();e.hasMoreElements();){
JarEntry JarEntry=e.nextElement();
字符串jarEntryName=jarEntry.getName();
字符串jarConnectionEntryName=jarConnection.getEntryName();
/**
*仅当文件与路径匹配时才提取文件。
*/
if(jarEntryName.startsWith(jarConnectionEntryName)){
String filename=jarEntryName.startsWith(jarConnectionEntryName)?jarEntryName.substring(jarConnectionEntryName.length()):jarEntryName;
File currentFile=新文件(destDir,文件名);
if(jarEntry.isDirectory()){
currentFile.mkdirs();
}否则{
InputStream=jarFile.getInputStream(jarEntry);
OutputStream out=FileUtils.openOutputStream(currentFile);
IOUtils.copy(is,out);
is.close();
out.close();
}
}
}
}捕获(IOE异常){
//TODO添加记录器
e、 printStackTrace();
}
}

lpiepiora的答案是正确的!但有一个小问题, 源代码应该是一个jar Url。当源路径是文件系统的路径时,上述代码将无法正常工作。 要解决此问题,您应该使用ReferencePath,代码可以从以下链接获得: 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"));
}
公共类资源目录{
public static void copyFromJar(最终字符串源路径,最终路径目标)抛出URISyntaxException,
IOException{
最终路径引用PathReference=PathReference.getPath(新URI(sourcePath));
最终路径jarPath=pathReference.getPath();
walkFileTree(jarPath,新的SimpleFileVisitor(){
专用路径目标;
@凌驾
公共文件VisitResult preVisitDirectory(最终路径目录,最终基本文件属性属性属性)引发IOException{
currentTarget=target.resolve(jarPath.relativize(dir)
  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;
  }
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;
        }

    });
}
copyFromJar("/path/to/the/template/in/jar", Paths.get("/tmp/from-jar"))
 /**
 * 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<JarEntry> 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();
    }

}
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"));
}
        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);
    }
}
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;
import java.util.Enumeration;
import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * A helper to copy resources from a JAR file into a directory.
 */
public final class ResourceCopy {

    /**
     * URI prefix for JAR files.
     */
    private static final String JAR_URI_PREFIX = "jar:file:";

    /**
     * The default buffer size.
     */
    private static final int BUFFER_SIZE = 8 * 1024;

    /**
     * Copies a set of resources into a temporal directory, optionally preserving
     * the paths of the resources.
     * @param preserve Whether the files should be placed directly in the
     *  directory or the source path should be kept
     * @param paths The paths to the resources
     * @return The temporal directory
     * @throws IOException If there is an I/O error
     */
    public File copyResourcesToTempDir(final boolean preserve,
        final String... paths)
        throws IOException {
        final File parent = new File(System.getProperty("java.io.tmpdir"));
        File directory;
        do {
            directory = new File(parent, String.valueOf(System.nanoTime()));
        } while (!directory.mkdir());
        return this.copyResourcesToDir(directory, preserve, paths);
    }

    /**
     * Copies a set of resources into a directory, preserving the paths
     * and names of the resources.
     * @param directory The target directory
     * @param preserve Whether the files should be placed directly in the
     *  directory or the source path should be kept
     * @param paths The paths to the resources
     * @return The temporal directory
     * @throws IOException If there is an I/O error
     */
    public File copyResourcesToDir(final File directory, final boolean preserve,
        final String... paths) throws IOException {
        for (final String path : paths) {
            final File target;
            if (preserve) {
                target = new File(directory, path);
                target.getParentFile().mkdirs();
            } else {
                target = new File(directory, new File(path).getName());
            }
            this.writeToFile(
                Thread.currentThread()
                    .getContextClassLoader()
                    .getResourceAsStream(path),
                target
            );
        }
        return directory;
    }

    /**
     * Copies a resource directory from inside a JAR file to a target directory.
     * @param source The JAR file
     * @param path The path to the directory inside the JAR file
     * @param target The target directory
     * @throws IOException If there is an I/O error
     */
    public void copyResourceDirectory(final JarFile source, final String path,
        final File target) throws IOException {
        final Enumeration<JarEntry> entries = source.entries();
        final String newpath = String.format("%s/", path);
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            if (entry.getName().startsWith(newpath) && !entry.isDirectory()) {
                final File dest =
                    new File(target, entry.getName().substring(newpath.length()));
                final File parent = dest.getParentFile();
                if (parent != null) {
                    parent.mkdirs();
                }
                this.writeToFile(source.getInputStream(entry), dest);
            }
        }
    }

    /**
     * The JAR file containing the given class.
     * @param clazz The class
     * @return The JAR file or null
     * @throws IOException If there is an I/O error
     */
    public Optional<JarFile> jar(final Class<?> clazz) throws IOException {
        final String path =
            String.format("/%s.class", clazz.getName().replace('.', '/'));
        final URL url = clazz.getResource(path);
        Optional<JarFile> optional = Optional.empty();
        if (url != null) {
            final String jar = url.toString();
            final int bang = jar.indexOf('!');
            if (jar.startsWith(ResourceCopy.JAR_URI_PREFIX) && bang != -1) {
                optional = Optional.of(
                    new JarFile(
                        jar.substring(ResourceCopy.JAR_URI_PREFIX.length(), bang)
                    )
                );
            }
        }
        return optional;
    }

    /**
     * Writes an input stream to a file.
     * @param input The input stream
     * @param target The target file
     * @throws IOException If there is an I/O error
     */
    private void writeToFile(final InputStream input, final File target)
        throws IOException {
        final OutputStream output = Files.newOutputStream(target.toPath());
        final byte[] buffer = new byte[ResourceCopy.BUFFER_SIZE];
        int length = input.read(buffer);
        while (length > 0) {
            output.write(buffer, 0, length);
            length = input.read(buffer);
        }
        input.close();
        output.close();
    }

}