Como obter um caminho para um recurso em um arquivo Java JAR


166

Estou tentando encontrar um caminho para um Recurso, mas não tive sorte.

Isso funciona (no IDE e com o JAR), mas desta forma não consigo encontrar o caminho para um arquivo, apenas o conteúdo do arquivo:

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

Se eu fizer isso:

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

O resultado é: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

Existe uma maneira de obter um caminho para um arquivo de recurso?


1
Sim. Eu tenho uma classe com a qual gostaria de trabalhar, uma pasta do lado de fora (no caso de eu querer alterar algum parâmetro do arquivo de configuração) e um JAR que oculta os arquivos de configuração da implementação para o usuário (como um distribuidor JAR a todas as pessoas).
No_ripcord

1
Portanto, a classe apenas recebe um PATH para um arquivo (o arquivo de configuração).
#

3
Então você provavelmente deve lidar com essa classe com um fluxo de entrada, que você pode obter de qualquer fonte.
242 Carl Manaster

1
Sim eu conheço. Mas teria sido mais claro e limpo do outro lado. Mas thx de qualquer maneira.
no_ripcord

1
Você está tentando fazer algo como a opção 4 nesta pergunta? stackoverflow.com/questions/775389/…
erickson

Respostas:


71

Isso é deliberado. O conteúdo do "arquivo" pode não estar disponível como um arquivo. Lembre-se de que você está lidando com classes e recursos que podem fazer parte de um arquivo JAR ou outro tipo de recurso. O carregador de classes não precisa fornecer um identificador de arquivo para o recurso, por exemplo, o arquivo jar pode não ter sido expandido em arquivos individuais no sistema de arquivos.

Tudo o que você pode fazer obtendo um arquivo java.io.File pode ser feito copiando o fluxo em um arquivo temporário e fazendo o mesmo, se um arquivo java.io.File for absolutamente necessário.


6
você pode adicionar 'rsrc:' ao chamar seu recurso para abri-lo. como novo File ( "rsrc: filename.txt"), este irá carregar filename.txt que é embalado dentro da raiz do seu jar
gipsh

63

Ao carregar um recurso, observe a diferença entre:

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

e

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

Eu acho que essa confusão está causando a maioria dos problemas ao carregar um recurso.


Além disso, quando você está carregando uma imagem, é mais fácil usar getResourceAsStream():

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

Quando você realmente precisa carregar um arquivo (sem imagem) de um arquivo JAR, pode tentar o seguinte:

File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile("tempfile", ".tmp");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}

if (file != null && !file.exists()) {
    throw new RuntimeException("Error: File " + file + " not found!");
}

3
+1 Isso funcionou para mim. Certifique-se de colocar os arquivos que deseja ler na binpasta e vá para o diretório da classe que carrega os recursos antes de usar o caminho `/com/myorg/filename.ext '.
rayryeng

+1 Isso também funciona para mim ... Eu entendo que pode haver alguns riscos de segurança relacionados a essa abordagem, portanto, os proprietários de aplicativos precisam estar cientes disso.
lucasa

você poderia esclarecer esta afirmação "É sempre melhor carregar um recurso com getResourceAsStream ()"? Como isso pode fornecer uma solução para o problema?
Luca S.

@LucaS. Isso foi feito apenas para o caso de imagens, desculpe não estar muito claro. A abordagem deve ser independente da plataforma, embora seja um pouco invasiva.
Tombart 3/11

@LucasA Você poderia esclarecer os riscos que mencionou?
ZX9 26/01

25

A resposta de uma linha é -

String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()

Basicamente, o getResourcemétodo fornece a URL. A partir desta URL, você pode extrair o caminho chamandotoExternalForm()

Referências:

getResource () , toExternalForm ()


7
Durante a execução no meu ambiente (IntelliJ), isso produz uma URL de arquivo simples que funciona em todos os casos. No entanto, ao executar a partir do próprio jar, recebo um URI semelhante ao jar: file: /path/to/jar/jarname.jar! /File_in_jar.mp4. Nem tudo pode utilizar um URI que começa com jar. Caso em questão JavaFX Media.
Noah Ternullo

1
Eu gosto mais desta resposta. Concedido, na maioria dos casos, pode ser preferível simplesmente pegar o InputStream no recurso quando ele estiver em um arquivo jar, mas se por algum motivo você realmente precisar do caminho, é isso que funciona. Eu precisava do caminho para dar a um objeto de terceiros. Então obrigado!
Mario

1
a solução acima não funciona, enquanto em IDE, em Intellijit acrescenta file:/ao percurso, que trabalha no frasco, mas não no IDE
Tayab Hussain

Sua resposta resolveu um problema que eu estava tentando resolver há pelo menos 2 dias. TYVM !!
Jonathan

12

Passei um tempo brincando com esse problema, porque nenhuma solução que encontrei realmente funcionou, por incrível que pareça! O diretório de trabalho freqüentemente não é o diretório do JAR, especialmente se um JAR (ou qualquer outro programa) for executado no menu Iniciar do Windows. Então, aqui está o que eu fiz, e ele funciona para arquivos .class executados de fora de um JAR, assim como funciona para um JAR. (Eu só testei no Windows 7.)

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}

8

se netclient.pestiver dentro de um arquivo JAR, ele não terá um caminho porque esse arquivo está localizado dentro de outro arquivo. Nesse caso, o melhor caminho que você pode ter é realmente file:/path/to/jarfile/bot.jar!/config/netclient.p.


Quando tento converter uma URL desse formato (... bot.jar! / Config / ...) em URI, ele diz que o caminho não é hierárquico.
gEdringer

7

Você precisa entender o caminho dentro do arquivo jar.
Basta referir-se a ele relativo. Portanto, se você possui um arquivo (myfile.txt), localizado em foo.jar, no \src\main\resourcesdiretório (estilo maven). Você se referiria a ele como:

src/main/resources/myfile.txt

Se você despejar seu jar usando jar -tvf myjar.jar , verá a saída e o caminho relativo dentro do arquivo jar e use o FORWARD SLASHES.


Você realmente precisa usar barras, mesmo no Windows. Isso implica que você não pode usar File.separator.
str

3

No meu caso, usei um objeto URL em vez de Path.

Arquivo

File file = new File("my_path");
URL url = file.toURI().toURL();

Recurso no caminho de classe usando o classloader

URL url = MyClass.class.getClassLoader().getResource("resource_name")

Quando preciso ler o conteúdo, posso usar o seguinte código:

InputStream stream = url.openStream();

E você pode acessar o conteúdo usando um InputStream.


3

É o mesmo código do usuário Tombart com fluxo liberado e fechado para evitar a cópia incompleta do conteúdo do arquivo temporário do recurso jar e ter nomes de arquivos temporários exclusivos.

File file = null;
String resource = "/view/Trial_main.html" ;
URL res = getClass().getResource(resource);
if (res.toString().startsWith("jar:")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile(new Date().getTime()+"", ".html");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.flush();
        out.close();
        input.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}
         

2

Um arquivo é uma abstração para um arquivo em um sistema de arquivos, e os sistemas de arquivos não sabem nada sobre o que é o conteúdo de um JAR.

Tente com um URI, acho que existe um protocolo jar: // que pode ser útil para seus proponentes.



1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Obter isso getSystemResourceAsStreamé a melhor opção. Ao obter o fluxo de entrada em vez do arquivo ou da URL, funciona em um arquivo JAR e como autônomo.


1

Pode ser um pouco tarde, mas você pode usar minha biblioteca KResourceLoader para obter um recurso do seu jar:

File resource = getResource("file.txt")

0

Quando em um arquivo jar, o recurso está localizado absolutamente na hierarquia de pacotes (não na hierarquia do sistema de arquivos). Portanto, se você tiver a classe com.example.Sweet carregando um recurso chamado "./default.conf", o nome do recurso será especificado como "/com/example/default.conf".

Mas se estiver em uma jarra, não será um arquivo ...


0

Dentro da pasta de recursos (java / main / resources) do seu jar, adicione seu arquivo (assumimos que você adicionou um arquivo xml chamado imports.xml ), depois injete ResourceLoaderse usar Spring como abaixo

@Autowired
private ResourceLoader resourceLoader;

escreva o código abaixo para carregar o arquivo:

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}

0

Talvez este método possa ser usado para solução rápida.

public class TestUtility
{ 
    public static File getInternalResource(String relativePath)
    {
        File resourceFile = null;
        URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
        String codeLocation = location.toString();
        try{
            if (codeLocation.endsWith(".jar"){
                //Call from jar
                Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
                resourceFile = path.toFile();
            }else{
                //Call from IDE
                resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
            }
        }catch(URISyntaxException ex){
            ex.printStackTrace();
        }
        return resourceFile;
    }
}

Você está omitindo algum contexto? Isso me dá:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Allen Luce

Eu editei a resposta e escreveu todo o método que eu usei em um software
gbii

0

siga o código!

/ src / main / resources / file

streamToFile(getClass().getClassLoader().getResourceAsStream("file"))

public static File streamToFile(InputStream in) {
    if (in == null) {
        return null;
    }

    try {
        File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        f.deleteOnExit();

        FileOutputStream out = new FileOutputStream(f);
        byte[] buffer = new byte[1024];

        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        return f;
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        return null;
    }
}
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.