2

I reviewed a lot of answers on the topic of Java Files and path problems, even some with whitespaces in the path, but non answered my question I'd like to ask:

Why does the following code work, when in a non-whitespace-directory and why does it fail otherwise?

I stripped my problem down to the bare essentials of the question, so I hope anyone can explain the problem to me ;-)

I have an old API that I must feed with the file handles of some images, and I decided to load the images via a relative path URL approach.

I know that this does not work from jars, but I do not intend to load any image from a jar yet. The solution is sufficient for my current requirements and I do not want to turn everything into streams as of now...

But the success of any execution is highly dependent on the directory it is done in! This seems strange to me.

So I wrote a JUnit test and provided some files which are in the test resources directory to research this issue.

└───imageload    
    ├───src
    │   ├───main
    │   │   ├───java
    │   │   └───resources
    │   └───test
    │       ├───java
    │       │   └───stupid
    │       │       └───test
    │       └───resources
    │           └───testset
    └───target
        ├───generated-test-sources
        │   └───test-annotations
        └───test-classes
            ├───stupid
            │   └───test
            └───testset

This is the test code to make the problem obvious:

package stupid.test;

[... imports omitted...]

public class SimpleTest {

    public static final File getFileByRelativePathURL(String relativePath) {
        URL url = Thread.currentThread().getContextClassLoader().getResource(relativePath);
        File file = new File(url.getPath());
        return file;
    }

    @Test
    void loadImage() {

        File theFile = SimpleTest.getFileByRelativePathURL("testset/black.jpg");
        Boolean exists = theFile.exists();

        assertTrue(exists);
    }


}

When running the test in this directory all works fine:

  • D:\Temp\working\imageload\

while running it from here it fails:

  • D:\Temp\not working\imageload\

I have had a closer look at the resulting file instances, and the space in the directory is properly escaped to this file object path:

file = D:\Temp\not%20working\imageload\target\test-classes\testset\black.jpg

But there must be a problem with the %20 because the file cannot be found according to the "exists()" method of the File Class.

I hope the problem might be obvious to many of you, while I still expect that "theFile.exists()" returns true in any directory with or without whitespaces.

What am I doing wrong?

UPDATE: I was asked if my question is a duplicate to this article:

Unfortunately the solution does not address my urge to find the proper relative path. Instead it passes an absolute path to the file constructor.

But the combination with the comment by JB Nizet helped me understanding the problem... and although it is a pretty dirty solution I changed my relative URL method to this:

public static final File getFileByRelativePathURL(String relativePath) {
        URL url = Thread.currentThread().getContextClassLoader().getResource(relativePath);
        File file = new File(url.getPath().replaceAll("%20", " "));
        return file;
    }

Now everything works fine... stupid me! I really have to do it in another way ;-)

LastZolex
  • 158
  • 2
  • 9
  • Just stop treating classpath resource URLs as file paths. Do the correct thing, and it will work fine. Return a URL, or an InputStream, instead of a File. – JB Nizet Jun 29 '19 at 12:23
  • good point, but as the API I try to feed with files does not like streams how can I avoid the problem? I know the questions seems stupid but this is what I want to solve... – LastZolex Jun 29 '19 at 12:28
  • 2
    If the API needs files just to be able to read from them, it's badly designed, and should take an InputStream as argument. You should fix (or ask a fix) for the API. If the API needs files to write to them, then you shouldn't use classpath resources to feed the API, but instead actual files on the file system. You can always copy the resources to a file before feeding the API if you want a file with a default content. – JB Nizet Jun 29 '19 at 12:41
  • that is a good advice! I see what I can do about it! At least I will try to work with streams throughout my own code! Thanks a lot for helping! :) – LastZolex Jun 29 '19 at 12:46
  • I think it will make sense to post your "update" as an answer – janos Jun 29 '19 at 13:52
  • ok, great, I will do so. I was not sure if this is good style ;-) – LastZolex Jun 29 '19 at 15:01

1 Answers1

0

Thanks to the comment of JB Nizet I came to a solution which is probably more a "dirty hack" then a proper workaround but it is an efficient solution for my current problem.

The original error was my assumption that I could use escaped URL paths as file paths, which is obviously not correct. So I removed the escaping of blanks by modifying my relative path method like this:

public static final File getFileByRelativePathURL(String relativePath) {
        URL url = Thread.currentThread().getContextClassLoader().getResource(relativePath);
        File file = new File(url.getPath().replaceAll("%20", " "));
        return file;
    }

This makes real spaces out of the '%20' substitutions and now the JUnit tests work in all paths that contain spaces. It is not a solution for other whitespaces or escaped special characters though.

I will take the advice and try to work around the necessity of File objects in the old API I am using as good as possible!

In my code I will definitely favour Streams! :)

LastZolex
  • 158
  • 2
  • 9