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.