55

While trying to copy some files in my jar file to a temp directory with my java app, the following exception is thrown:

java.nio.file.FileSystemNotFoundException
    at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171)
    at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157)
    at java.nio.file.Paths.get(Unknown Source)
    at com.sora.util.walltoggle.pro.WebViewPresentation.setupTempFiles(WebViewPresentation.java:83)
   ....

and this is a small part of my setupTempFiles(with line numbers):

81. URI uri = getClass().getResource("/webViewPresentation").toURI();
//prints: URI->jar:file:/C:/Users/Tom/Dropbox/WallTogglePro.jar!/webViewPresentation
82. System.out.println("URI->" + uri );
83. Path source = Paths.get(uri);

the webViewPresentation directory resides in the root directory of my jar:

enter image description here

This problem only exits when I package my app as a jar, debugging in Eclipse has no problems. I suspect that this has something to do with this bug but I'm not sure how to correct this problem.

Any helps appreciated

If matters:

I'm on Java 8 build 1.8.0-b132

Windows 7 Ult. x64

tom91136
  • 7,875
  • 11
  • 53
  • 72
  • You have to do it slightly differently for jars http://stackoverflow.com/questions/5171957/access-file-in-jar-file – onesixtyfourth Mar 24 '14 at 09:42
  • The method used in the answer reads a single file as `Stream`, I want to copy the entire directory. I've add a screenshot to clarify – tom91136 Mar 24 '14 at 09:46
  • Maybe you also want to use [Files.createTempDirectory](http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#createTempDirectory%28java.nio.file.Path,%20java.lang.String,%20java.nio.file.attribute.FileAttribute...%29) – SebastianH Mar 24 '14 at 10:11
  • Yes I did that somewhere in my `main()` – tom91136 Mar 24 '14 at 10:12
  • While running in your IDE (Eclipse) the resource is actualy a file. If you package the application and try to run it outside the IDE, the problem will surface. – cquezel Oct 05 '20 at 15:18

4 Answers4

59

A FileSystemNotFoundException means the file system cannot be created automatically; and you have not created it here.

Given your URI, what you should do is split against the !, open the filesystem using the part before it and then get the path from the part after the !:

final Map<String, String> env = new HashMap<>();
final String[] array = uri.toString().split("!");
final FileSystem fs = FileSystems.newFileSystem(URI.create(array[0]), env);
final Path path = fs.getPath(array[1]);

Note that you should .close() your FileSystem once you're done with it.

fge
  • 110,072
  • 26
  • 223
  • 312
  • This works until I try to copy or resolve using the path : `path.resolve(...) or path.relativize(...)`, it throws `java.nio.file.ProviderMismatchException` – tom91136 Mar 24 '14 at 10:14
  • 4
    Yes, that is something which is not very well documented... You should `.resolve()` or `.relativize()` the `.toString()` values of other paths if these paths are not from the same filesystem provider... I have been bitten by that too (I am developing a FileSystem implementation over FTP) – fge Mar 24 '14 at 10:24
  • Note that if you are interested I have implemented a `FileVisitor` which does recursive copying; I have successfully used it to create entire zip files from an existing directory – fge Mar 24 '14 at 10:34
  • 1
    Not sure it is any better but [here you go](http://fgaliegue.blogspot.com/2014/03/working-with-java-7-file-api-recursive.html) :) – fge Mar 24 '14 at 13:55
  • 2
    Note that since then I have developed something that may interest you; see [here](https://github.com/fge/java7-fs-more) – fge Jan 09 '15 at 21:05
  • Not working for me. Gives "java.nio.file.ProviderNotFoundException: Provider "zip" not found" when creating the newFileSystem. The given URI is zip:/u01/oratst/middleware/Weblogic_Home/user_projects/domains/x/y/_wl_cls_gen.jar – Theodore K. Nov 18 '15 at 10:18
  • @8odoros that is normal; the scheme of the provider is `jar`, not `zip`. Don't ask me why. – fge Nov 18 '15 at 10:20
  • @fge It did work with replaceAll("zip", "jar:file"); ;-) – Theodore K. Nov 18 '15 at 12:24
  • 1
    @8odoros you'd better use .replaceFirst(); you only want to replace the first occurrence you find, not all – fge Nov 18 '15 at 12:25
  • 2
    Can this approach work with nested jars, e.g. `jar:file:outer.jar!/inner.jar!/file.txt`? – jaco0646 Jun 01 '16 at 20:26
  • This works only _within_ the `jar`. Now imagine the code is used within a `@Service` or `@Component` that is instantiated during a Maven `test` or `install`: The path will not contain any `!`, `array[0]` will be thus empty and the build will fail complaining that `Path component should be '/'`. – Christoph Möbius Aug 31 '16 at 09:38
  • @ChristophMöbius I have no idea what a `@Service` or `@Component` is so I don't see how your comment is any relevant to the discussion at hand? – fge Aug 31 '16 at 11:19
  • @fge These annotations come with the Spring Framework and enable auto-detection of classes during initialization, i.e. they can be `@Autowire`d to other classes. (if you ever want to employ IOC/dependency injection you need to use these.) – Christoph Möbius Sep 01 '16 at 08:25
8

Accepted answer isn't the best since it doesn't work when you start application in IDE or resource is static and stored in classes! Better solution was proposed at java.nio.file.FileSystemNotFoundException when getting file from resources folder

InputStream in = getClass().getResourceAsStream("/webViewPresentation");
byte[] data = IOUtils.toByteArray(in);

IOUtils is from Apache commons-io.

But if you are already using Spring and want a text file you can change the second line to

StreamUtils.copyToString(in, Charset.defaultCharset());

StreamUtils.copyToByteArray also exists.

Nathan Tuggy
  • 2,239
  • 27
  • 28
  • 36
Kinmarui
  • 545
  • 7
  • 16
  • 4
    The OP was trying to get a `Path` (presumably for the new async processing capabilities in NIO), not a stream or a string, so this is not the answer. – kaqqao Aug 29 '17 at 14:22
  • It might not be the answer to the questions but indeed the path/uri solution is unusable for two reasons: It does not work if resources are unpacked and it does not work if you open two resources from the jar simultanously - you can open the file system only once ... however how do you know in general if it is the same file system? Lots of tests are necessary. The answer here shows a simple and working solution. – Claude Nov 24 '18 at 01:13
5

This is maybe a hack, but the following worked for me:

URI uri = getClass().getResource("myresourcefile.txt").toURI();

if("jar".equals(uri.getScheme())){
    for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
        if (provider.getScheme().equalsIgnoreCase("jar")) {
            try {
                provider.getFileSystem(uri);
            } catch (FileSystemNotFoundException e) {
                // in this case we need to initialize it first:
                provider.newFileSystem(uri, Collections.emptyMap());
            }
        }
    }
}
Path source = Paths.get(uri);

This uses the fact that ZipFileSystemProvider internally stores a List of FileSystems that were opened by URI.

fuemf5
  • 161
  • 2
  • 4
-1

If you're using spring framework library, then there is an easy solution for it.

As per requirement we want to read webViewPresentation; I could solve the same problem with below code:

URI uri = getClass().getResource("/webViewPresentation").toURI();
FileSystems.getDefault().getPath(new UrlResource(uri).toString());
hemanto
  • 1,262
  • 11
  • 15