Pergunta

Estou tentando acessar um arquivo XML dentro de um arquivo jar, de um frasco separado que está sendo executado como uma aplicação desktop. Posso obter o URL para a necessidade de arquivo I, mas quando eu passar isso para um FileReader (como String) eu recebo um FileNotFoundException dizendo: "O nome do arquivo, nome do diretório ou sintaxe de rótulo de volume está incorreta."

Como um ponto de referência, não tenho recursos de imagem de leitura de problemas do mesmo frasco, passando o URL para um construtor ImageIcon. Isto parece indicar que o método que estou usando para obter o URL está correto.

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

Dentro da classe ServicesLoader Tenho

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

O que há de errado em usar esta técnica para ler o arquivo XML?

Foi útil?

Solução 4

O problema foi que eu estava indo longe demais em chamar o método de análise de XMLReader. O método de análise aceita um InputSource, então não havia nenhuma razão para sequer usar um FileReader. Alterar a última linha do código acima para

xr.parse( new InputSource( filename ));

funciona muito bem.

Outras dicas

Parece que você quer usar java.lang.Class.getResourceAsStream(String), veja

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

Você não diz se este é um aplicativo de desktop ou web. Gostaria de usar o método getResourceAsStream() de um ClassLoader apropriado se é uma área de trabalho ou o contexto se é um aplicativo web.

Parece que você está usando o resultado URL.toString como o argumento para o construtor FileReader. URL.toString é um pouco quebrado, e em vez disso você deve geralmente usar url.toURI().toString(). Em qualquer caso, a cadeia não é um caminho de arquivo.

Em vez disso, você deve ser:

  • Passe o URL para ServicesLoader e deixá-lo chamar openStream ou similar.
  • Use Class.getResourceAsStream e apenas passar o fluxo ao longo, possivelmente dentro de um InputSource. (Lembre-se de verificar se há valores nulos como o API é um pouco confuso.)

Eu gostaria de salientar que um questões é o que se os mesmos recursos estão em vários arquivos JAR. Vamos dizer que você quer ler /org/node/foo.txt, mas não a partir de um arquivo, mas a partir de cada arquivo jar.

Eu executar para esse mesmo problema várias vezes antes. Eu estava esperando no JDK 7 que alguém iria escrever um sistema de arquivos classpath, mas ainda infelizmente não.

A mola tem a classe de recurso que lhe permite recursos de classpath carga muito bem.

Eu escrevi um pequeno protótipo para resolver este problema muito de ler recursos formar vários arquivos JAR. O protótipo não lidar com cada caso extremo, mas ele lida com procurando recursos em diretórios que estão nos arquivos jar.

Eu tenho usado Stack Overflow durante bastante tempo. Esta é a segunda resposta que eu me lembro de responder a uma pergunta então me perdoe se eu for muito longo (que é minha natureza).

Este é um leitor de recursos protótipo. O protótipo é desprovido de verificação de erros robusto.

Eu tenho dois arquivos protótipo jar que eu tenho configuração.

 <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>

O frasco arquivos cada um tem um arquivo sob / org / node / chamada resource.txt.

Este é apenas um protótipo do que um manipulador ficaria com classpath: // Eu também tenho um resource.foo.txt em meus recursos locais para este projeto.

Ele pega tudo para cima deles e os imprime.

   

    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();
                    }

                }

            }
        }

    }


   

Você pode ver um exemplo mais completo aqui com a saída de amostra.

Fora de sua técnica, porque não usar o Java padrão jarfile classe para obter as referências que você quer? De lá, a maioria de seus problemas devem desaparecer.

Se você usar recursos extensivamente, você pode considerar o uso Commons VFS .

Também suporta: * Ficheiros locais * FTP, SFTP * HTTP e HTTPS * Arquivos Temporários "FS normais suportada) * Postal, o frasco e alcatrão (não comprimido, tgz ou tbz2) * Gzip e bzip2 * Recursos * Ram - "ramdrive" * Mime

Há também JBoss VFS -. Mas não é pouco documentada

Eu tenho 2 arquivos CSV que eu uso para ler os dados. O programa java é exportado como um arquivo jar executável. Quando você exportá-lo, você vai descobrir que não exporta seus recursos com ele.

Eu adicionei uma pasta em projeto chamado dados em eclipse. Nessa pasta i armazenados meus arquivos CSV.

Quando eu precisar fazer referência a esses arquivos que eu fazê-lo assim ...

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;
}

Então, quando você colocar o jar em um local para que ele possa ser executado via linha de comando, certifique-se de adicionar a pasta de dados com os recursos para o mesmo local que o arquivo jar.

Aqui está um código de exemplo sobre como ler um arquivo adequadamente dentro de um arquivo jar (neste caso, a corrente de execução arquivo jar)

Apenas mudança executável com o caminho do seu arquivo jar se não for o atual em execução.

Em seguida, mudar o filePath para o caminho do arquivo que você deseja usar dentro do arquivo jar. OU SEJA se o arquivo está em

someJar.jar \ img \ Test.gif

. Defina o filePath para "img \ Test.gif"

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();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top