Recurso Java como arquivo


122

Existe uma maneira em Java para construir uma instância de arquivo em um recurso recuperado de um jar através do carregador de classe?

Meu aplicativo usa alguns arquivos do jar (padrão) ou de um diretório do sistema de arquivos especificado em tempo de execução (entrada do usuário). Estou procurando uma maneira consistente de:
a) carregar esses arquivos como um fluxo
b) listar os arquivos no diretório definido pelo usuário ou no diretório no jar, respectivamente

Edit: Aparentemente, a abordagem ideal seria ficar longe do java.io.File completamente. Existe uma maneira de carregar um diretório do caminho de classe e listar seu conteúdo (arquivos / entidades nele contidos)?

Respostas:


58

ClassLoader.getResourceAsStreame Class.getResourceAsStreamsão definitivamente o caminho a percorrer para carregar os dados do recurso. No entanto, não acredito que exista alguma maneira de "listar" o conteúdo de um elemento do caminho de classe.

Em alguns casos, isso pode ser simplesmente impossível - por exemplo, um ClassLoader pode gerar dados em tempo real, com base no nome do recurso solicitado. Se você olhar para a ClassLoaderAPI (que é basicamente o mecanismo do caminho de classe), verá que não há nada para fazer o que deseja.

Se você souber que realmente possui um arquivo jar, poderá carregá-lo ZipInputStreampara descobrir o que está disponível. Isso significa que você terá um código diferente para diretórios e arquivos jar.

Uma alternativa, se os arquivos forem criados separadamente primeiro, é incluir uma espécie de arquivo de manifesto contendo a lista de recursos disponíveis. Empacote isso no arquivo jar ou inclua-o no sistema de arquivos como um arquivo e carregue-o antes de oferecer ao usuário uma escolha de recursos.


3
Concordo que é irritante - mas torna o ClassLoader mais amplamente aplicável de outras maneiras. Por exemplo, é fácil escrever um "carregador de classes da web" porque a web é boa para buscar arquivos, mas normalmente não lista os arquivos.
31909 Jon Skeet

Parece que se o recurso que você está tentando carregar for muito maior que 1MiB, InputStreamvocê obtém as getResourceAsStreamparadas para recuperar os bytes do recurso após esse tamanho e, em vez disso, retorna 0 se estiver contido em um sistema de arquivos compactado como um jar. Você parece ser forçado a usar getResourcee carregar o arquivo independentemente disso nesse caso.
mgttlinger

1
@mgttlinger: Isso me parece improvável ... provavelmente vale a pena postar uma nova pergunta com uma reprodução, se puder.
21815 Jon Skeet

O problema ocorreu devido ao readmétodo que decide quantos dados ler (eu não sabia disso). E parece que o arquivo inteiro é lido se o arquivo que está sendo lido estiver localizado em uma pasta regular. Se o arquivo estiver dentro de um jar porque foi empacotado, ele lerá apenas partes dele.
mgttlinger

1
@mgttlinger: Não, ele não lê apenas partes dele - mas pode não preencher todo o buffer fornecido em todas as chamadas de leitura. Como sempre InputStream, se você quiser ler todo o recurso, faça um loop até readretornar -1.
precisa saber é o seguinte

98

Eu tive o mesmo problema e pude usar o seguinte:

// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()

45
Esta abordagem não funciona com JARs no classpath, apenas com arquivos e diretórios
yegor256

1
@ yegor256 Se o seu arquivo estiver em uma jarra, você poderá criar um FileSystemobjeto e obter o resultado Pathdisso. Então você pode ler como de costume. (consulte stackoverflow.com/a/22605905/1876344 )
mgttlinger

53

Aqui está um pouco do código de um dos meus aplicativos ... Deixe-me saber se ele atende às suas necessidades. Você pode usar isso se souber o arquivo que deseja usar.

URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());

Espero que ajude.


Sim, mas toURI()falha.
Olivier Faucheux

10

Uma maneira confiável de construir uma instância de arquivo em um recurso recuperado de um jar é copiar o recurso como um fluxo para um arquivo temporário (o arquivo temporário será excluído quando a JVM sair):

public static File getResourceAsFile(String resourcePath) {
    try {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (in == null) {
            return null;
        }

        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        tempFile.deleteOnExit();

        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            //copy stream
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

4

Tente o seguinte:

ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

Existem mais métodos disponíveis, por exemplo, consulte aqui: http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html


Obrigado pela sua resposta. Eu sei sobre getResourceAsStream, mas acho que não consigo carregar um diretório do caminho de classe por meio disso.
Mantrum 24/03/09

No diretório Java existe um arquivo (raízes do UNIX, eu acho), então você deve pelo menos tentar.
Topchef 24/03/09

Eu acho que para listar os arquivos em um diretório que você precisará usar java.io.File. Você pode procurar arquivos com o ClassLoader.findResource, que retorna uma URL que pode ser passada para o arquivo.
Nathan Voxland 24/03/09

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.