【问题标题】:How to copy resources folder out of jar into program files如何将资源文件夹从jar复制到程序文件中
【发布时间】:2016-07-14 07:15:08
【问题描述】:

我花了好几个小时寻找答案,但我无法弄清楚,我正在尝试复制我的资源文件夹,其中包含我正在运行的游戏的所有图像和数据文件罐子和成 E:/Program Files/mtd/ 当我从 eclipse 中运行它时它工作正常,但是当我导出 jar 并尝试它时,我得到 NoSuchFileException

`JAR
Installing...
file:///C:/Users/Cam/Desktop/mtd.jar/resources to file:///E:/Program%20Files/mtd
/resources
java.nio.file.NoSuchFileException: C:\Users\Cam\Desktop\mtd.jar\resources
        at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
        at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
        at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
        at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Sou
rce)
        at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Sou
rce)
        at sun.nio.fs.WindowsFileSystemProvider.readAttributes(Unknown Source)
        at java.nio.file.Files.readAttributes(Unknown Source)
        at java.nio.file.FileTreeWalker.walk(Unknown Source)
        at java.nio.file.FileTreeWalker.walk(Unknown Source)
        at java.nio.file.Files.walkFileTree(Unknown Source)
        at java.nio.file.Files.walkFileTree(Unknown Source)
        at me.Zacx.mtd.main.Game.<init>(Game.java:94)
        at me.Zacx.mtd.main.Game.main(Game.java:301)`

这是我正在使用的代码:

    if (!pfFolder.exists()) {
    pfFolder.mkdir();
    try {

        URL url = getClass().getResource("/resources/");
        URI uri = null;

        if (url.getProtocol().equals("jar")) {
            System.out.println("JAR");
            JarURLConnection connect = (JarURLConnection) url.openConnection();
            uri = new URI(connect.getJarFileURL().toURI().toString() + "/resources/");

        } else if (url.getProtocol().equals("file")) {
            System.out.println("FILE");
            uri = url.toURI();
        }

        final Path src = Paths.get(uri);
        final Path tar = Paths.get(System.getenv("ProgramFiles") + "/mtd/resources/");

        System.out.println("Installing...");
        System.out.println(src.toUri() + " to " + tar.toUri());

        Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
            public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) throws IOException {
                return copy(file);
            }
            public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) throws IOException {
                return copy(dir);
            }
            private FileVisitResult copy( Path fileOrDir ) throws IOException {
                System.out.println("Copying " + fileOrDir.toUri() + " to " + tar.resolve( src.relativize( fileOrDir ) ).toUri());
                Files.copy( fileOrDir, tar.resolve( src.relativize( fileOrDir ) ) );
                return FileVisitResult.CONTINUE;
            }
        });
        System.out.println("Done!");
    } catch (IOException e) {
        e.printStackTrace();
    } catch (URISyntaxException e) {
        e.printStackTrace();
    }
}

【问题讨论】:

  • %20 是一个 HTML 转义空间,是不是给你带来了问题(可能来自通过.toUri() 的 URL 编码)?第 94 行具体在哪里? (Game.java:94) C:\Users\Cam\Desktop\mtd.jar\resources 是文件夹还是文件?
  • @MaximilianGerhardt 使用 .replaceAll(...) 删除 %20s 并不能解决问题,Game.java:94 是 Files.walkFileTree(src, new SimpleFileVisitor() { 和resources 是一个文件夹,其中包含我的游戏的所有图像等。抱歉没有包括这些:/
  • 那么,Java 说java.nio.file.NoSuchFileException: C:\Users\Cam\Desktop\mtd.jar\resources 不正确吗,因为\resources 确实是一个文件夹,没有目录?您遍历文件树的方式一定有一些错误。也许尝试先创建创建文件夹?您的 copy() 函数似乎没有被调用,否则我们会看到 Copying .. to ... 输出行 OR 它在 .resolve()relativize() 的确切 System.out.println() 行中崩溃功能。添加更多调试输出以确保。
  • @MaximilianGerhardt 问题是它没有看到 jar 里面的资源文件夹,文件夹是 100% 在那里,java 似乎看不到它,我不知道为什么因为它是一个文件夹而不是一个文件,所以我必须做一些不同的事情吗?
  • 它在 Eclipse 中工作的原因是所有资源都作为单独的文件存在于文件系统中,因此您始终遵循file 协议路径。当所有东西都打包到罐子里时,你不能只是遍历它;标准的树遍历只会将 jar 报告为常规文件并继续进行下一个条目。您必须将 jar 作为自己的文件系统“打开”才能在其中进行树遍历。

标签: java eclipse file program-files


【解决方案1】:

这里的问题是不同的文件系统。 C:/Users/Cam/Desktop/mtd.jarWindowsFileSystem 中的File。因为它是一个文件,而不是一个目录,所以你不能访问文件内的子目录;如果mtd.jar 实际上是目录 而不是文件C:/Users/Cam/Desktop/mtd.jar/resources 仅是有效的Path

为了访问不同文件系统上的内容,您必须使用该文件系统根目录的路径。例如,如果您在D:\dir1\dir2\file 中有一个文件,则无法使用以C:\ 开头的路径访问它(不支持符号链接);您必须使用从该文件系统的根目录开始的路径 D:\

jar 文件只是一个文件。它可以位于文件系统中的任何位置,并且可以像任何常规文件一样移动、复制或删除。但是,它本身包含自己的文件系统。没有可用于引用 jar 文件系统内任何文件的 windows 路径,就像没有以 C:\ 开头的路径可以引用 D:\ 文件系统内的任何文件一样。

要访问 jar 的内容,您必须以 ZipFileSystem 的身份打开该 jar。

// Autoclose the file system at end of try { ... } block.
try(FileSystem zip_fs = FileSystems.newFileSystem(pathToZipFile, null)) {
}

拥有zip_fs 后,您可以使用zip_fs.getPath("/path/in/zip");Path 获取到其中的文件。这个Path 对象实际上是ZipFileSystemProvider 路径对象,而不是WindowsFileSystemProvider 路径对象,否则它是一个Path 对象,可以打开、读取等,至少在ZipFileSystem 之前已经关闭。最大的区别在于path.getFileSystem() 将返回ZipFileSystem,而resolve()relativize() 不能使用getFileSystem() 返回不同文件系统的路径对象。

当您的项目从 Eclipse 运行时,所有资源都在 WindowsFileSystem 中,因此遍历文件系统树并复制资源非常简单。当您的项目从 jar 运行时,资源不在默认文件系统中。

这是一个将资源复制到安装目录的 Java 类。它可以在 Eclipse 中工作(所有资源都作为单独的文件),以及将应用程序打包到 jar 中时。

public class Installer extends SimpleFileVisitor<Path> {

    public static void installResources(Path dst, Class<?> cls, String root) throws URISyntaxException, IOException {
        URL location = cls.getProtectionDomain().getCodeSource().getLocation();
        if (location.getProtocol().equals("file")) {
            Path path = Paths.get(location.toURI());
            if (location.getPath().endsWith(".jar")) {
                try (FileSystem fs = FileSystems.newFileSystem(path, null)) {
                    installResources(dst, fs.getPath("/" + root));
                }
            } else {
                installResources(dst, path.resolve(root));
            }
        } else {
            throw new IllegalArgumentException("Not supported: " + location);
        }
    }

    private static void installResources(Path dst, Path src) throws IOException {
        Files.walkFileTree(src, new Installer(dst, src));
    }

    private final Path target, source;

    private Installer(Path dst, Path src) {
        target = dst;
        source = src;
    }

    private Path resolve(Path path) {
        return target.resolve(source.relativize(path).toString());
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        Path dst = resolve(dir);
        Files.createDirectories(dst);
        return super.preVisitDirectory(dir, attrs);
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        Path dst = resolve(file);
        Files.copy(Files.newInputStream(file), dst, StandardCopyOption.REPLACE_EXISTING);
        return super.visitFile(file, attrs);
    }
}

称为:

    Path dst = Paths.get("C:\\Program Files\\mtd");
    Installer.installResources(dst, Game.class, "resources");

【讨论】:

  • 当我尝试这个解决方案时,我得到这个输出file:///C:/Users/Cam/Desktop/eclipse/Work/Meme%20Tower%20Defence/bin/ to file:///E:/Program%20Files/mtd/resources file:/C:/Users/Cam/Desktop/eclipse/Work/Meme%20Tower%20Defence/bin/ Exception in thread "main" java.nio.file.ProviderNotFoundException: Provider not found 当前代码:System.out.println(src.toUri() + " to " + tar.toUri()); System.out.println(uri); try (FileSystem fs = FileSystems.newFileSystem(Paths.get(uri), null)) { Path path = fs.getPath("/resources/"); Files.walkFileTree(path, new SimpleFileVisitor&lt;Path&gt;() {
  • 你有一个uri 到罐子里?试试FileSystems.newFileSystem(uri, new HashMap&lt;&gt;()); 版本的调用。
  • 我不确定你的意思,你能详细说明一下吗?再次抱歉,我是文件系统的新手。
  • @Zacx 我已经扩展了我的例子来展示一个例子。您必须添加资源复制。
【解决方案2】:

我终于找到了答案 我不想打出冗长的解释,但对于任何寻找解决方案的人来说,这里是

 `  
              //on startup
               installDir("");
                for (int i = 0; i < toInstall.size(); i++) {
                    File f = toInstall.get(i);
                    String deepPath = f.getPath().replace(f.getPath().substring(0, f.getPath().lastIndexOf("resources") + "resources".length() + 1), "");
                    System.out.println(deepPath);
                    System.out.println("INSTALLING: " + deepPath);
                    installDir(deepPath);
                    System.out.println("INDEX: " + i);
                }

public void installDir(String path) {
            System.out.println(path);
            final URL url = getClass().getResource("/resources/" + path);
            if (url != null) {
                try {
                    final File apps = new File(url.toURI());
                    for (File app : apps.listFiles()) {
                        System.out.println(app);
                            System.out.println("copying..." + app.getPath() + " to " + pfFolder.getPath());
                            String deepPath = app.getPath().replace(app.getPath().substring(0, app.getPath().lastIndexOf("resources") + "resources".length() + 1), "");
                            System.out.println(deepPath);

                        try {

                                File f = new File(resources.getPath() + "/" + deepPath);
                                if (getExtention(app) != null) {
                                FileOutputStream resourceOS = new FileOutputStream(f);
                                byte[] byteArray = new byte[1024];
                                int i;
                                InputStream classIS = getClass().getClassLoader().getResourceAsStream("resources/" + deepPath);
                        //While the input stream has bytes
                                while ((i = classIS.read(byteArray)) > 0) 
                                {
                        //Write the bytes to the output stream
                                    resourceOS.write(byteArray, 0, i);
                                }
                        //Close streams to prevent errors
                                classIS.close();
                                resourceOS.close();
                                } else {
                                    System.out.println("new dir: " + f.getPath() + " (" + toInstall.size() + ")");
                                    f.mkdir();
                                    toInstall.add(f);
                                    System.out.println(toInstall.size());
                                }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (URISyntaxException ex) {
                    // never happens
                }
            }

        }`

【讨论】:

    【解决方案3】:

    这比我想象的要难,但这里是如何做到的。

    这里是我的复制方法参考https://examples.javacodegeeks.com/core-java/io/file/4-ways-to-copy-file-in-java/

    public void copyFile(String inputPath, String outputPath ) throws IOException
    {
    
        InputStream inputStream = null;
    
        OutputStream outputStream = null;
        try {
    
            inputStream =  getClass().getResourceAsStream(inputPath);
            outputStream = new FileOutputStream(outputPath);
    
            byte[] buf = new byte[1024];
    
            int bytesRead;
    
            while ((bytesRead = inputStream.read(buf)) > 0) {
    
                outputStream.write(buf, 0, bytesRead);
    
           }
    
        }
        finally {
    
    
            inputStream.close();
    
            outputStream.close();
    
        }
    

    请注意本图中Jar文件的项目结构Project structure

    现在我需要读取 Jar 文件。这是此解决方案 How can I get a resource "Folder" from inside my jar File? 的一个变体。这两种方法一起工作以产生结果。我已经对此进行了测试,并且可以正常工作。

    公共类主{

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
    
        final String pathPartOne = "test/com";
        final String pathPartTwo = "/MyResources";
        String pathName = "C:\\Users\\Jonathan\\Desktop\\test.jar";
    
        JarTest test = new JarTest();
    
        final File jarFile = new File(pathName);
    
        if(jarFile.isFile()) {  // Run with JAR file
            final JarFile jar = new JarFile(jarFile);
            final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
            while(entries.hasMoreElements()) {
                final String name = entries.nextElement().getName();
    
                if (name.startsWith(pathPartOne+pathPartTwo + "/")) { //filter according to the path
                    if(name.contains("."))//has extension
                    {
                        String relavtivePath = name.substring(pathPartOne.length()+1);
                        String fileName = name.substring(name.lastIndexOf('/')+1);
                        System.out.println(relavtivePath);
                        System.out.println(fileName);
                        test.copyFile(relavtivePath, "C:\\Users\\Jonathan\\Desktop\\" + fileName);
                    }
    
                }
            }
            jar.close();
        } 
    
    
    }
    

    }

    希望对您有所帮助。

    【讨论】:

    • 这将读取一个文件,我对图像等做了同样的事情,但是我试图将一个文件夹从我的 jar 复制到我的 jar 之外的目录。
    • 是的,但我相信同样的原则也适用于此。您首先需要通过获取路径来获取要复制的文件。一旦你有了路径,你应该被排序。我不相信“C:\Users\Cam\Desktop\mtd.jar\resources”是有效路径
    • 理论上,它应该是一个有效路径,因为在 mtd.jar 中有一个名为 resources 的文件夹,对不起,如果我完全关闭,我是文件系统库的新手,我'米 14 所以我没有多年的经验可以用来解决这个问题:(
    • 看看stackoverflow.com/questions/11012819/…,一旦你有了文件夹,剩下的应该很容易
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-31
    • 1970-01-01
    • 2015-01-12
    • 2023-01-11
    • 1970-01-01
    相关资源
    最近更新 更多