Como posso obter uma "Pasta" de recurso dentro do meu arquivo jar?

139

Eu tenho uma pasta / pacote de recursos na raiz do meu projeto, "não" quero carregar um determinado arquivo. Se eu quisesse carregar um determinado arquivo, usaria class.getResourceAsStream e ficaria bem !! Na verdade, o que eu quero fazer é carregar uma "Pasta" dentro da pasta de recursos, fazer um loop nos arquivos dentro dessa pasta e obter um fluxo para cada arquivo e ler o conteúdo ... Suponha que os nomes dos arquivos não sejam determinados antes do tempo de execução ... O que devo fazer? Existe uma maneira de obter uma lista dos arquivos dentro de uma pasta no seu arquivo jar? Observe que o arquivo Jar com os recursos é o mesmo arquivo jar a partir do qual o código está sendo executado ...

Desde já, obrigado...

Mostafa Zeinali
fonte
1
Isso ajudaria: stackoverflow.com/questions/1529611/…
Jigar Joshi
Veja também: stackoverflow.com/questions/4764347/…
Jigar Joshi
5
O primeiro é sobre a extração de um arquivo jar, que é a última coisa que quero tentar, como uma situação de pior a pior !! Se eu quiser extrair os arquivos, eu os colocaria na minha pasta de instalação no momento da instalação! Eu quero acessá-los de dentro do jar, e minha razão é que, se getResourceAsStream puder acessar um arquivo, o Java também poderá explorar pastas nesse jar, sem a necessidade de extraí-lo !! Mas obrigado ...
Mostafa Zeinali

Respostas:

111

Finalmente, encontrei a solução:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) {  // Run with JAR file
    final JarFile jar = new JarFile(jarFile);
    final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
    while(entries.hasMoreElements()) {
        final String name = entries.nextElement().getName();
        if (name.startsWith(path + "/")) { //filter according to the path
            System.out.println(name);
        }
    }
    jar.close();
} else { // Run with IDE
    final URL url = Launcher.class.getResource("/" + path);
    if (url != null) {
        try {
            final File apps = new File(url.toURI());
            for (File app : apps.listFiles()) {
                System.out.println(app);
            }
        } catch (URISyntaxException ex) {
            // never happens
        }
    }
}

O segundo bloco funciona quando você executa o aplicativo no IDE (não com o arquivo jar). Você pode removê-lo se não gostar disso.

user1079877
fonte
Atualizado a resposta correta. Embora não seja isso que eu gostaria de fazer, especialmente em um código com mais de 34000 arquivos de origem ... Mas parece ser a única solução. Obrigado.
Mostafa Zeinali
4
FYI, isso não funciona se iniciado a partir do webstart porque getPath retorna algo relativo ao domínio do servidor.
amos
1
Não acho que isso vai funcionar se o caminho está em um frasco, dará este erro na conversão touri: java.lang.IllegalArgumentException: URI não é hierárquica
tribbloid
4
Só funciona para arquivo-jar única, não trabalho para recursos de extrato de dependências em outro frasco
tribbloid
2
Esta não é uma solução segura, porque: 1. Supõe que o arquivo .jar é um arquivo local no mesmo sistema; 2. URL.getPath () não retorna um nome de arquivo válido, apenas retorna a parte do caminho da URL, com todas as porcentagens de escape intactas; 3. ProtectionDomain.getCodeSource () pode retornar nulo .
VGR
14

Tente o seguinte.
Crie o caminho do recurso "<PathRelativeToThisClassFile>/<ResourceDirectory>" Por exemplo, se o caminho da sua classe for com.abc.package.MyClass e seus arquivos de pesquisa estiverem em src / com / abc / package / resources /:

URL url = MyClass.class.getResource("resources/");
if (url == null) {
     // error - missing folder
} else {
    File dir = new File(url.toURI());
    for (File nextFile : dir.listFiles()) {
        // Do something with nextFile
    }
}

Você também pode usar

URL url = MyClass.class.getResource("/com/abc/package/resources/");
Glen Best
fonte
27
Obrigado pelo tempo e esforço, mas isso NÃO funciona quando sua base de código está em um arquivo .jar e seus recursos também estão nesse arquivo .jar. Quando você está dentro de um arquivo .jar, não pode criar um File () e também não pode obter um URL para essa pasta ... Eu, pessoalmente, fiquei em paz com ele e apenas listei cada arquivo em um XML ... t acha que pode fazer isso para uma pasta dentro de um arquivo jar em tudo ...
Mostafa Zeinali
2
Desculpe que você esteja tendo problemas. No entanto, ele realmente funciona quando sua base de código está em um arquivo jar e seus recursos também estão nesse arquivo jar. Você não está criando um arquivo () no disco - você está lendo o arquivo no arquivo jar na memória java. Observe que o ClassLoader está feliz em tratar um arquivo jar equivalente a um diretório no disco. Aqui estão os detalhes da fonte:
Glen Best
1
Apenas executei o código em uma jarra e recebi o erro ... oops. V desculpe, meu mal. Precisa lidar com o caminho da URL jar com "Arquivo: ...! / Dir1 / dir2 ...". Duas correções: stackoverflow.com/questions/1429172/… OR String jarPath = dirURL.getPath (). Substring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = novo JarFile (URLDecoder.decode (jarPath, "UTF-8")); Enumeração <JarEntry> entradas = jar.entries (); while (entries.hasMoreElements ()) {// fazer alguma coisa}
Glen Melhor
3
A partir do javadoc, é óbvio que o método nem sempre garante um arquivo retornado em todas as circunstâncias. MAS, se você realmente ler o Q, o OP terá 100% de garantia de trabalhar com arquivos e pastas por construção. O Q está pedindo para obter acesso a diretórios e arquivos - isso é mais específico que a abstração generalizada da API. A conversão em arquivo é apropriada e correta para atender à necessidade. Leia o Q corretamente antes de postar comentários fortemente contrários. Felicidades.
Glen Melhor
7
O OP não está funcionando com arquivos e diretórios. Ele está trabalhando com recursos em um arquivo JAR. Os recursos em um arquivo JAR não são arquivos ou diretórios e não podem ser listados com a Fileclasse. Este código não funciona com recursos dentro de um arquivo JAR. Período.
Marquês de Lorne
7

Eu sei que isso é há muitos anos. Mas apenas para outras pessoas se deparam com esse tópico. O que você pode fazer é usar o getResourceAsStream()método com o caminho do diretório e o fluxo de entrada terá todos os nomes de arquivos desse diretório. Depois disso, você pode concat o caminho do diretório com cada nome de arquivo e chamar getResourceAsStream para cada arquivo em um loop.

Cheng Lin
fonte
7
Isso funciona bem, a menos que você planeje disfarçar as coisas, como se vê.
Tustin2121
1
Eu acho que essa deve ser a solução aceita, pois é limpa e não requer manipulação de versões jar e IDE em casos separados. Embora eu não saiba por que o getResource não encontra a pasta, mas o getResourceAsStream o encontra.
Raghuram Onti Srinivasan
6

Eu tive o mesmo problema em mãos enquanto tentava carregar algumas configurações do hadoop a partir de recursos empacotados no jar ... no IDE e no jar (versão de lançamento).

Eu achei java.nio.file.DirectoryStreamque funcionava melhor para iterar o conteúdo do diretório tanto no sistema de arquivos local quanto no jar.

String fooFolder = "/foo/folder";
....

ClassLoader classLoader = foofClass.class.getClassLoader();
try {
    uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
    throw new FooException(e.getMessage());
} catch (NullPointerException e){
    throw new FooException(e.getMessage());
}

if(uri == null){
    throw new FooException("something is wrong directory or files missing");
}

/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
    /** jar case */
    try{
        URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
        //jar.toString() begins with file:
        //i want to trim it out...
        Path jarFile = Paths.get(jar.toString().substring("file:".length()));
        FileSystem fs = FileSystems.newFileSystem(jarFile, null);
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
        for(Path p: directoryStream){
            InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
        performFooOverInputStream(is);
        /** your logic here **/
            }
    }catch(IOException e) {
        throw new FooException(e.getMessage());     
    }
}
else{
    /** IDE case */
    Path path = Paths.get(uri);
    try {
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
        for(Path p : directoryStream){
            InputStream is = new FileInputStream(p.toFile());
            performFooOverInputStream(is);
        }
    } catch (IOException _e) {
        throw new FooException(_e.getMessage());
    }
}
Injeção SQL
fonte
Isso funcionou bem pra mim. Somente troquei o 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' com 'Caminho jarFile = Paths.get (jar.toURI ());' para que ele funcione no Windows. Também usou uma tentativa com instrução de recurso para o objeto FileSystem.
Jarle Jacobsen
Isso funcionou para mim! Eu estava executando o aplicativo dropwizard na janela de encaixe.
nIKUNJ
4

O código a seguir retorna a "pasta" desejada como Path, independentemente de estar dentro de um jar ou não.

  private Path getFolderPath() throws URISyntaxException, IOException {
    URI uri = getClass().getClassLoader().getResource("folder").toURI();
    if ("jar".equals(uri.getScheme())) {
      FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
      return fileSystem.getPath("path/to/folder/inside/jar");
    } else {
      return Paths.get(uri);
    }
  }

Requer java 7+.

hummingV
fonte
Agradável! Devo acrescentar que o FileSystem é Closeable, o que significa que você precisa fechá-lo em algum momento para liberar recursos do sistema. Obviamente, o Path retornado se tornará inválido imediatamente depois disso.
Kirill Gamazkov
Observe que, no caso de um arquivo jar, o nome do recurso (aqui pacote + folder) parece ser ignorado ao criar o sistema de arquivos. Portanto getPath, não retorna folder/path/to/..., mas apenas path/to/....
Marcono1234
1

Outra solução, você pode fazer isso usando ResourceLoaderassim:

import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;

@Autowire
private ResourceLoader resourceLoader;

...

Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
    load(fi.next())
}
Qy Zuo
fonte
0

Simples ... use OSGi. No OSGi, você pode iterar as entradas do seu Bundle com findEntries e findPaths.

Peter Kriens
fonte
10
Se envolver OSGI, dificilmente chamaria de 'simples'. Mas se você já estiver usando OSGI ... sim, com certeza. Mas sugerir mudar para a OSGI apenas para resolver esse problema em particular parece um pouco demais.
Kris
O OSGi também resolve muitos outros problemas e hoje em dia é muito fácil começar. Veja os tutoriais do OSGi enRoute
Peter Kriens
Eu ficaria longe do OSGi, a menos que você já precise usá-lo. Bloatware superprojetado que resolve os problemas de implantação da IMO dos anos 90.
user2337270
-1

Dentro do meu arquivo jar, eu tinha uma pasta chamada Upload, essa pasta tinha outros três arquivos de texto e precisava ter exatamente a mesma pasta e arquivos fora do arquivo jar, usei o código abaixo:

URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);

URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);

URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);
LoveLovelyJava
fonte
1
Olá, obrigado pela resposta, mas, você vê, você precisava saber o nome de cada arquivo dentro dessa pasta ao escrever o código; e você teve que nomear cada um explicitamente. O que eu procurava era um método para iterar os arquivos dentro da pasta e obter seus nomes em tempo de execução, para que eu pudesse adicionar recursos à pasta, sem alterar o código, sabendo que o código também os processaria. Obrigado pelo seu tempo. : 3
Mostafa Zeinali
-1

Como as outras respostas apontam, quando os recursos estão dentro de um arquivo jar, as coisas ficam realmente feias. No nosso caso, esta solução:

https://stackoverflow.com/a/13227570/516188

funciona muito bem nos testes (já que, quando os testes são executados, o código não é compactado em um arquivo jar), mas não funciona quando o aplicativo realmente é executado normalmente. Então, o que eu fiz foi ... Codifico a lista dos arquivos no aplicativo, mas tenho um teste que lê a lista real do disco (pode fazê-lo, pois funciona nos testes) e falha se a lista real não corresponde à lista que o aplicativo retorna.

Dessa forma, tenho um código simples no meu aplicativo (sem truques) e tenho certeza de que não esqueci de adicionar uma nova entrada na lista, graças ao teste.

Emmanuel Touzery
fonte
-25

Este link mostra como.

A mágica é o método getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")
James Anderson
fonte
20
Não, meu!! Eu não quero um único arquivo !! Eu quero uma pasta inteira, cujos subarquivos são pré-tempo indeterminado !!!
Mostafa Zeinali