Java jarファイルからリソースファイルを読み取るにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/403256

  •  03-07-2019
  •  | 
  •  

質問

デスクトップアプリケーションとして実行されている別のjarから、jarファイル内のXMLファイルにアクセスしようとしています。必要なファイルのURLを取得できますが、それをFileReaderに(Stringとして)渡すと、「ファイル名、ディレクトリ名、またはボリュームラベルの構文が正しくありません」というFileNotFoundExceptionが表示されます。

参考として、同じjarからイメージリソースを読み取り、URLをImageIconコンストラクターに渡すのに問題はありません。これは、URLを取得するために使用しているメソッドが正しいことを示しているようです。

URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );

私が持っているServicesLoaderクラスの内部

XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));

この手法を使用してXMLファイルを読み取ることの何が問題になっていますか

役に立ちましたか?

解決 4

問題は、XMLReaderのparseメソッドを呼び出す際に、私が一歩踏み出しすぎていたことです。 parseメソッドはInputSourceを受け入れるため、FileReaderを使用する理由さえありませんでした。上記のコードの最終行を

に変更します
xr.parse( new InputSource( filename ));

正常に動作します。

他のヒント

java.lang.Class.getResourceAsStream(String)を使用するように見えます。参照してください

http ://java.sun.com/javase/6/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

これがデスクトップアプリかWebアプリかはわかりません。デスクトップの場合は適切なClassLoaderの getResourceAsStream()メソッドを使用し、Webアプリの場合はContextを使用します。

FileReader コンストラクターの引数として URL.toString の結果を使用しているように見えます。 URL.toString は少し壊れているため、通常は url.toURI()。toString()を使用する必要があります。いずれにしても、文字列はファイルパスではありません。

代わりに、次のいずれかを実行する必要があります。

  • URL ServicesLoader に渡し、 openStream などを呼び出します。
  • Class.getResourceAsStream を使用して、おそらく InputSource 内でストリームを渡します。 (APIは少し複雑なので、nullをチェックすることを忘れないでください。)

1つの問題は、同じリソースが複数のjarファイルにある場合にどうなるかを指摘したいと思います。 /org/node/foo.txtを読みたいが、1つのファイルからではなく、各jarファイルから読みたいとしましょう。

この問題に何度か遭遇しました。 JDK 7では、誰かがクラスパスファイルシステムを記述することを期待していましたが、残念ながらまだです。

Springには、クラスパスリソースを非常にうまくロードできるResourceクラスがあります。

複数のjarファイルからリソースを読み取るというこの問題を解決するために、小さなプロトタイプを作成しました。プロトタイプはすべてのエッジケースを処理するわけではありませんが、jarファイル内のディレクトリ内のリソースの検索を処理します。

かなり以前からStack Overflowを使用しています。これは質問への回答を覚えている2番目の回答です。長すぎた場合はご容赦ください(私の性質です)。

これはプロトタイプのリソースリーダーです。プロトタイプには堅牢なエラーチェックがありません。

セットアップした2つのプロトタイプjarファイルがあります。

 <pre>
         <dependency>
              <groupId>invoke</groupId>
              <artifactId>invoke</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>

          <dependency>
               <groupId>node</groupId>
               <artifactId>node</artifactId>
               <version>1.0-SNAPSHOT</version>
          </dependency>

jarファイルにはそれぞれ、/ org / node /の下にresource.txtというファイルがあります。

これは、ハンドラがclasspath://でどのように見えるかのプロトタイプです。 また、このプロジェクトのローカルリソースにresource.foo.txtがあります。

すべてをピックアップして印刷します。

   

    package com.foo;

    import java.io.File;
    import java.io.FileReader;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.net.URI;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;

    /**
    * Prototype resource reader.
    * This prototype is devoid of error checking.
    *
    *
    * I have two prototype jar files that I have setup.
    * <pre>
    *             <dependency>
    *                  <groupId>invoke</groupId>
    *                  <artifactId>invoke</artifactId>
    *                  <version>1.0-SNAPSHOT</version>
    *              </dependency>
    *
    *              <dependency>
    *                   <groupId>node</groupId>
    *                   <artifactId>node</artifactId>
    *                   <version>1.0-SNAPSHOT</version>
    *              </dependency>
    * </pre>
    * The jar files each have a file under /org/node/ called resource.txt.
    * <br />
    * This is just a prototype of what a handler would look like with classpath://
    * I also have a resource.foo.txt in my local resources for this project.
    * <br />
    */
    public class ClasspathReader {

        public static void main(String[] args) throws Exception {

            /* This project includes two jar files that each have a resource located
               in /org/node/ called resource.txt.
             */


            /* 
              Name space is just a device I am using to see if a file in a dir
              starts with a name space. Think of namespace like a file extension 
              but it is the start of the file not the end.
            */
            String namespace = "resource";

            //someResource is classpath.
            String someResource = args.length > 0 ? args[0] :
                    //"classpath:///org/node/resource.txt";   It works with files
                    "classpath:///org/node/";                 //It also works with directories

            URI someResourceURI = URI.create(someResource);

            System.out.println("URI of resource = " + someResourceURI);

            someResource = someResourceURI.getPath();

            System.out.println("PATH of resource =" + someResource);

            boolean isDir = !someResource.endsWith(".txt");


            /** Classpath resource can never really start with a starting slash.
             * Logically they do, but in reality you have to strip it.
             * This is a known behavior of classpath resources.
             * It works with a slash unless the resource is in a jar file.
             * Bottom line, by stripping it, it always works.
             */
            if (someResource.startsWith("/")) {
                someResource = someResource.substring(1);
            }

              /* Use the ClassLoader to lookup all resources that have this name.
                 Look for all resources that match the location we are looking for. */
            Enumeration resources = null;

            /* Check the context classloader first. Always use this if available. */
            try {
                resources = 
                    Thread.currentThread().getContextClassLoader().getResources(someResource);
            } catch (Exception ex) {
                ex.printStackTrace();
            }

            if (resources == null || !resources.hasMoreElements()) {
                resources = ClasspathReader.class.getClassLoader().getResources(someResource);
            }

            //Now iterate over the URLs of the resources from the classpath
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();


                /* if the resource is a file, it just means that we can use normal mechanism
                    to scan the directory.
                */
                if (resource.getProtocol().equals("file")) {
                    //if it is a file then we can handle it the normal way.
                    handleFile(resource, namespace);
                    continue;
                }

                System.out.println("Resource " + resource);

               /*

                 Split up the string that looks like this:
                 jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
                 into
                    this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
                 and this
                     /org/node/
                */
                String[] split = resource.toString().split(":");
                String[] split2 = split[2].split("!");
                String zipFileName = split2[0];
                String sresource = split2[1];

                System.out.printf("After split zip file name = %s," +
                        " \nresource in zip %s \n", zipFileName, sresource);


                /* Open up the zip file. */
                ZipFile zipFile = new ZipFile(zipFileName);


                /*  Iterate through the entries.  */
                Enumeration entries = zipFile.entries();

                while (entries.hasMoreElements()) {
                    ZipEntry entry = entries.nextElement();
                    /* If it is a directory, then skip it. */
                    if (entry.isDirectory()) {
                        continue;
                    }

                    String entryName = entry.getName();
                    System.out.printf("zip entry name %s \n", entryName);

                    /* If it does not start with our someResource String
                       then it is not our resource so continue.
                    */
                    if (!entryName.startsWith(someResource)) {
                        continue;
                    }


                    /* the fileName part from the entry name.
                     * where /foo/bar/foo/bee/bar.txt, bar.txt is the file
                     */
                    String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);
                    System.out.printf("fileName %s \n", fileName);

                    /* See if the file starts with our namespace and ends with our extension.        
                     */
                    if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {


                        /* If you found the file, print out 
                           the contents fo the file to System.out.*/
                        try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) {
                            StringBuilder builder = new StringBuilder();
                            int ch = 0;
                            while ((ch = reader.read()) != -1) {
                                builder.append((char) ch);

                            }
                            System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", entryName, builder);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }

                    //use the entry to see if it's the file '1.txt'
                    //Read from the byte using file.getInputStream(entry)
                }

            }


        }

        /**
         * The file was on the file system not a zip file,
         * this is here for completeness for this example.
         * otherwise.
         *
         * @param resource
         * @param namespace
         * @throws Exception
         */
        private static void handleFile(URL resource, String namespace) throws Exception {
            System.out.println("Handle this resource as a file " + resource);
            URI uri = resource.toURI();
            File file = new File(uri.getPath());


            if (file.isDirectory()) {
                for (File childFile : file.listFiles()) {
                    if (childFile.isDirectory()) {
                        continue;
                    }
                    String fileName = childFile.getName();
                    if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {

                        try (FileReader reader = new FileReader(childFile)) {
                            StringBuilder builder = new StringBuilder();
                            int ch = 0;
                            while ((ch = reader.read()) != -1) {
                                builder.append((char) ch);

                            }
                            System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }

                    }

                }
            } else {
                String fileName = file.getName();
                if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {

                    try (FileReader reader = new FileReader(file)) {
                        StringBuilder builder = new StringBuilder();
                        int ch = 0;
                        while ((ch = reader.read()) != -1) {
                            builder.append((char) ch);

                        }
                        System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                }

            }
        }

    }


   

サンプル出力を使用して、より詳細な例を見ることができます。

テクニック以外で、標準のJava JarFileクラスを使用して、必要な参照を取得しますか?そこからほとんどの問題は解決します。

リソースを広範囲に使用する場合は、   Commons VFS

以下もサポートしています:  *ローカルファイル  * FTP、SFTP  * HTTPおよびHTTPS  *一時ファイル&quot;通常のFSバックアップ済み)  * Zip、JarおよびTar(非圧縮、tgzまたはtbz2)  * gzipおよびbzip2  *リソース  * ram-&quot; ramdrive&quot;  * mime

JBoss VFS もありますが、あまり文書化されていません。

データの読み取りに使用する2つのCSVファイルがあります。 Javaプログラムは、実行可能なjarファイルとしてエクスポートされます。エクスポートすると、リソースがエクスポートされないことがわかります。

日食のデータと呼ばれるプロジェクトの下にフォルダーを追加しました。そのフォルダにcsvファイルを保存しました。

これらのファイルを参照する必要がある場合、このようにします...

private static final String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";

private static String getFileLocation(){
    String loc = new File("").getAbsolutePath() + File.separatorChar +
        "data" + File.separatorChar;
    if (usePrimaryZipCodesOnly()){              
        loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY);
    } else {
        loc = loc.concat(ZIP_FILE_LOCATION);
    }
    return loc;
}

次に、コマンドライン経由で実行できるようにjarを場所に配置するときは、必ずjarファイルと同じ場所にリソースを含むデータフォルダーを追加してください。

これは、jarファイル(この場合は現在実行中のjarファイル)内のファイルを適切に読み取る方法のサンプルコードです。

実行中のjarファイルでない場合は、jarファイルのパスで実行可能ファイルを変更します。

次に、filePathをjarファイル内で使用するファイルのパスに変更します。 I.E.ファイルが

にある場合
  

someJar.jar \ img \ test.gif

。 filePathを&quot; img \ test.gif&quot;に設定します

File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];

int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
    fileInputStreamReader.read(bytes, offset, size);
    offset = sizeOrig - fileInputStreamReader.available();
    size = fileInputStreamReader.available();
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top