简体   繁体   中英

Why am I getting 'nullfiles' when trying to copy a file from inside a jar to disk?

I am attempting to copy a file from inside my JAR to disk, outside the JAR file. The files that I will need to copy are default configuration files for a large-scale accounting system and are needed on the computer file system.

I have searched StackOverflow, as well as other sites (found with Google) and have read around fifty answers, which I've tried all of them. The code below is the first that has not simply blown up (with NullPointerException or FileNotFoundException ), but has actually attempted to get the resource located in the JAR file.

I have my JAR file set up as follows:

  • com.is2300.isis
    • MainClass.java (actual name is crazy long and I don't want to type it out right now)
  • com.is2300.isis.resources
    • Location of the resource file I would like to copy out to disk
  • com.is2300.isis.utils
    • Location of my class ResourceExporter (below - bottom) that has the file exporting methods.

My MainClass.main() entry-point function:

    public static void main(String[] args) {
        // Test our 'utils.ResourceExporter.exportResource(String resourceName)
        //+ function.

        // Set the start point of our substring to either 5 or 9, depending upon
        //+ if we are debugging (in NetBeans) or not (executing the JAR).
        if ( isDebugging ) {
            startPoint = 5;
        } else {
            startPoint = 9;
        }

        // First, we'll try just the name of the resource file to export.
        String rsName = "nwind.conf";

        try {
            System.out.println(ResourceExporter.exportResource(rsName, 
                    MainClass.class, "/home/user/tmp", startPoint));
        } catch (Exception ex) {
            System.err.println(ex.getCause());
            System.err.println(ex.getMessage());
            ex.printStackTrace(System.err);
        }

        // Then, we'll try it with the absolute path.
        rsName = "/com/is2300/isis/resources/nwind.conf";

        try {
            System.out.println(ResourceExporter.exportResource(rsName, 
                    MainClass.class, "/home/user/tmp", startPoint));
        } catch (Exception ex) {
            System.err.println(ex.getCause());
            System.err.println(ex.getMessage());
            ex.printStackTrace(System.err);
        }

        // Then, we'll try it with the relative path.
        rsName = "../resources/nwind.conf";

        try {
            System.out.println(ResourceExporter.exportResource(rsName, 
                    MainClass.class, "/home/user/tmp", startPoint));
        } catch (Exception ex) {
            System.err.println(ex.getCause());
            System.err.println(ex.getMessage());
            ex.printStackTrace(System.err);
        }

        // Last, we'll try it using dots instead of slashes.
        rsName = "com.is2300.isis.resources.nwind.conf";

        try {
            System.out.println(ResourceExporter.exportResource(rsName, 
                    MainClass.class, "/home/user/tmp", startPoint));
        } catch (Exception ex) {
            System.err.println(ex.getCause());
            System.err.println(ex.getMessage());
            ex.printStackTrace(System.err);
        }

    }

My ResourceExporter.exportResource() method:

    public static String exportResource(String resourceName, Class cls, 
                    String outPath, int startPoint) throws Exception {
        File files = new File(cls.getResource(
                cls.getResource(cls.getSimpleName() +
                ".class").toString().substring(
                startPoint, cls.getResource(
                cls.getSimpleName() + ".class").toString().lastIndexOf("/")
                + 1)) + "files");

        InputStream in = new FileInputStream(files);
        FileOutputStream out = new FileOutputStream(outPath + 
                        resourceName.substring(resourceName.lastIndexOf("/")));

        int readBytes;
        byte[] buffer = new byte[4096];
        while ( (readBytes = in.read(buffer)) > 0 )
            out.write(buffer, 0, readBytes);

        in.close();
        out.close();

        return files.getAbsolutePath();
    }

With what I'm doing in public static void main(String[] args) , I would expect one of the calls to the ResourceExporter.exportResource() method to actually cause the file to be copied.

However, when I step through the exportResource() method, on each call after the line:

        File files = new File(cls.getResource(
                cls.getResource(cls.getSimpleName() +
                ".class").toString().substring(
                startPoint, cls.getResource(
                cls.getSimpleName() + ".class").toString().lastIndexOf("/")
                + 1)) + "files");

The variable files.getCanonicalPath() call shows /home/user/Projects/ProjectName/nullfiles and I do not understand why this is, nor what this is.

@JBNizet and @AndrewThompson:

Thank you both for your comments. Especially @JBNizet! You gave me a swift kick in the head that made me look closer at what I had written and immediately saw the issue. Thank you very much for that.

The fix was this: Instead of the convoluted thing I was doing:

        File files = new File(cls.getResource(
                cls.getResource(cls.getSimpleName() +
                ".class").toString().substring(
                startPoint, cls.getResource(
                cls.getSimpleName() + ".class").toString().lastIndexOf("/")
                + 1)) + "files");

        InputStream in = new FileInputStream(files);
        FileOutputStream out = new FileOutputStream(outPath + 
                        resourceName.substring(resourceName.lastIndexOf("/")));

Which I had written about 10 years ago and don't remember what I was thinking, I was able to simplify it to this:

        InputStream in = ClassLoader.getSystemClassLoader().getSystemResourceAsStream(resourceName);
        FileOutputStream out = new FileOutputStream(outPath + 
                        resourceName.substring(resourceName.lastIndexOf("/")));

Now, the "file" (as a nod to the semantic correction by @AndrewThompson) is being located within the JAR file.

However, that was not the only change that had to be made. Where I set up the variable that is passed into the parameter resourceName was also not correct. I had it set up like so:

String rsName = "/com/is2300/isis/resources/nwind.conf";

However, the leading slash (/) was not supposed to be there. As soon as I changed the above line to:

String rsName = "com/is2300/isis/resources/nwind.conf";

everything just worked the way I expected.

Again, thanks to those two who commented. I appreciate your thoughts and assistance in getting my brain engaged.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM