リソースでファイルを開く安全な方法は次のとおりです。
InputStream is = this.getClass().getResourceAsStream("/path/in/jar/file.name");
ここで問題は、私のファイルがWeka Wrapperパッケージのディサイダーのモデルであり、 Decider class にはメソッドしかないということです。
public void load(File file) throws Exception
loadはファイルを受け取り、それをFileInputStreamとして開きます。回避策はありますか?リソースに入れてモデルを出荷したいです。一時ファイルを作成し、モデルのコンテンツを一時ファイルに書き込んで、一時ファイルをWekaに渡すことを考えていましたが、それは非常にダーティです。
私は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
リソースをロードするClassLoaderが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;
}
}
}
Jar内のファイルを参照できるようにするcommons-vfsのようなサードパーティライブラリを使用することもできます。例えば。 jar:// Arch-file-uri[! absolute-path]
。 commons-vfsは、ファイルを表す独自の FileObject
を指定しているため、Decider.load(File)
APIに適応するには、コンテンツをローカルのJava.io.File
にコピーする必要があります。