使用 FileInputStream 加载资源中的文件

2022-09-03 13:45:14

我知道在资源中打开文件的安全方法是:

  InputStream is = this.getClass().getResourceAsStream("/path/in/jar/file.name");  

现在的问题是,我的文件是Weka Wrapper包中决策程序的模型,而Decider类只有一个方法:

  public void load(File file) throws Exception 

load 获取该文件并将其作为 FileInputStream 打开。您看到解决方法了吗?我真的很想发布模型,将其放入资源中。我正在考虑创建一个临时文件,将模型的内容写入临时文件,然后将临时文件传递给Weka,但它太脏了。其他选择?


答案 1

我看到2个解决方案:

解决方案 1

将类路径资源读取到临时文件,并在调用后将其删除load(File)

InputStream cpResource = this.getClass().getClassLoader().getResourceAsStream("file.name");
File tmpFile = File.createTempFile("file", "temp");
FileUtils.copyInputStreamToFile(cpResource, tmpFile); // FileUtils from apache-io
try {
    decider.load(tmpFile);
} finally {
    tmpFile.delete();
}

解决方案 2

如果加载资源的类加载器是 URLClassLoader,则可以尝试查找绝对文件名。但这仅在所需资源作为文件存在于文件系统上时才有效。如果文件包含在 jar 中,则不起作用。

ClassLoader classLoader = this.getClass().getClassLoader();
if(classLoader instanceof URLClassLoader){
    URLClassLoader urlClassLoader = URLClassLoader.class.cast(classLoader);
    URL resourceUrl = urlClassLoader.findResource("file.name");

    if("file".equals(resourceUrl.getProtocol())){
        URI uri = resourceUrl.toURI();
        File file = new File(uri);
        decider.load(file);
    }
}

我建议编写一个实用程序类,尝试通过类装入器查找绝对文件,或者如果它无法以这种方式获取它,请使用临时文件方法作为回退。

或者以更面向对象的方式:

    public class FileResourceTest {

    public static void main(String[] args) throws IOException {
        File resourceAsFile = getResourceAsFile("file.name");
        System.out.println(resourceAsFile);
    }

    private static File getResourceAsFile(String resource) throws IOException {
        ClassLoader cl = FileResourceTest.class.getClassLoader();
        File file = null;
        FileResource fileResource = new URLClassLoaderFileResource(cl, resource);
        try {
            file = fileResource.getFile();
        } catch (IOException e) {
            fileResource = new ClasspathResourceFileResource(cl, resource);
            file = fileResource.getFile();
        }
        return file;
    }

    public static interface FileResource {

        public File getFile() throws IOException;

    }

    public static class ClasspathResourceFileResource implements FileResource {

        private ClassLoader cl;
        private String resource;

        public ClasspathResourceFileResource(ClassLoader cl, String resource) {
            this.cl = cl;
            this.resource = resource;
        }

        public File getFile() throws IOException {
            InputStream cpResource = cl.getResourceAsStream(resource);
            File tmpFile = File.createTempFile("file", "temp");
            FileUtils.copyInputStreamToFile(cpResource, tmpFile);
            tmpFile.deleteOnExit();
            return tmpFile;
        }

    }

    public static class URLClassLoaderFileResource implements FileResource {

        private ClassLoader cl;
        private String resource;

        public URLClassLoaderFileResource(ClassLoader cl, String resourcePath) {
            this.cl = cl;
            this.resource = resourcePath;
        }

        public File getFile() throws IOException {
            File resourceFile = null;
            if (cl instanceof URLClassLoader) {
                URLClassLoader urlClassLoader = URLClassLoader.class.cast(cl);
                URL resourceUrl = urlClassLoader.findResource(resource);
                if ("file".equals(resourceUrl.getProtocol())) {
                    try {

                        URI uri = resourceUrl.toURI();
                        resourceFile = new File(uri);
                    } catch (URISyntaxException e) {
                        IOException ioException = new IOException(
                                "Unable to get file through class loader: "
                                        + cl);
                        ioException.initCause(e);
                        throw ioException;
                    }

                }
            }
            if (resourceFile == null) {
                throw new IOException(
                        "Unable to get file through class loader: " + cl);
            }
            return resourceFile;
        }

    }
}

您还可以使用第三方库,如commons-vfs,它允许您引用jar中的文件。例如.由于 commons-vfs 指定了自己的 FileObject 来表示文件,因此您仍必须将内容复制到本地以适应 API。jar:// arch-file-uri[! absolute-path]java.io.FileDecider.load(File)

编辑

这是非常有帮助的!在较新版本的java中,是否有任何内容已经支持此要求?

即使我没有看过每个新版本的每个类,我也会说不。因为类路径资源并不总是文件。例如,它可以是jar中的文件,甚至是远程资源。想想Java程序员很久以前使用的小程序。因此,类路径的概念及其资源不绑定到本地 filsystem。这显然是一件好事,因为您几乎可以从每个URL加载类,这使它更加灵活。但是,这种灵活性还意味着,如果需要 ,则必须读取资源并创建副本。File

但是,也许像我上面展示的那样的某种实用程序代码会进入JRE。也许它已经在那里了。如果是这样,请发表评论,让我们都知道。


答案 2

推荐