Java JAR 파일에서 자원의 경로를 얻는 방법


166

나는 자원의 길을 찾으려고 노력하고 있지만 운이 없었다.

이것은 작동하지만 (IDE와 JAR 모두에서) 작동하지만 파일 경로 만 얻을 수 없으며 파일 내용 만 얻을 수 있습니다.

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

내가 이렇게하면 :

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

결과는 다음과 같습니다. java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

리소스 파일의 경로를 얻는 방법이 있습니까?


1
예. 외부의 폴더 (구성 파일의 일부 매개 변수를 변경하려는 경우)와 구현 구성 파일을 사용자에게 숨기는 JAR (배포 가능 항목)과 함께 작업하고 싶은 클래스가 있습니다. 모든 사람에게 JAR).
no_ripcord

1
따라서 클래스는 파일 (구성 파일)에 대한 PATH를 수신합니다.
no_ripcord

3
그런 다음 해당 클래스가 입력 스트림을 처리해야하며 소스 중 하나에서 가져올 수 있습니다.
Carl Manaster

1
예, 알아요 그러나 다른 방법으로는 더 명확하고 깨끗했을 것입니다. 그러나 어쨌든 thx.
no_ripcord

1
이 질문에서 옵션 # 4와 같은 것을하려고합니까? stackoverflow.com/questions/775389/…
erickson 2016 년

답변:


71

이것은 의도적입니다. "파일"의 내용이 파일로 제공되지 않을 수 있습니다. JAR 파일이나 다른 종류의 리소스에 포함될 수있는 클래스와 리소스를 다루고 있음을 기억하십시오. 클래스 로더는 리소스에 파일 핸들을 제공 할 필요가 없습니다. 예를 들어, jar 파일은 파일 시스템에서 개별 파일로 확장되지 않았을 수 있습니다.

java.io.File이 필요한 경우 스트림을 임시 파일로 복사하고 동일한 작업을 수행하여 java.io.File을 가져 와서 수행 할 수있는 모든 작업을 수행 할 수 있습니다.


6
리소스를 열려면 'rsrc :'를 추가하면됩니다. new File ( "rsrc : filename.txt")처럼 jar의 루트 안에 압축 된 filename.txt를로드합니다
gipsh

63

리소스를로드 할 때 다음의 차이점을 확인하십시오.

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

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

이 혼란으로 인해 리소스를로드 할 때 대부분의 문제가 발생합니다.


또한 이미지를로드 할 때 사용하기가 더 쉽습니다 getResourceAsStream().

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

JAR 아카이브에서 (이미지가 아닌) 파일을 실제로로드해야 할 때 다음을 시도하십시오.

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 이것은 나를 위해 일했다. bin`/com/myorg/filename.ext '경로를 사용하기 전에 읽을 파일을 폴더에 넣고 리소스에있는 클래스 로딩 디렉토리로 이동하십시오.
rayryeng

+1 이것은 또한 저에게 효과적입니다.이 접근 방식과 관련하여 일부 보안 위험이있을 수 있음을 이해합니다. 따라서 응용 프로그램 소유자는이를 알고 있어야합니다.
LucasA

이 문장을 명확히 할 수 있습니까? "getResourceAsStream ()으로 항상 리소스를로드하는 것이 좋습니다"? 이것이 어떻게 문제에 대한 해결책을 제공 할 수 있습니까?
Luca S.

@LucaS. 그것은 단지 이미지의 경우를위한 것이 었습니다. 미안하지만 명확하지 않았습니다. 접근 방식은 플랫폼에 독립적이어야하지만 약간 해킹 적입니다.
Tombart

@LucasA 언급 한 위험을 명확하게 설명해 주시겠습니까?
ZX9

25

한 줄의 대답은-

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

기본적으로 getResource메소드는 URL을 제공합니다. 이 URL에서 호출하여 경로를 추출 할 수 있습니다toExternalForm()

참고 문헌 :

getResource () , toExternalForm ()


7
내 환경 (IntelliJ)에서 실행하는 동안 모든 경우에 작동하는 간단한 파일 URL이 생성됩니다. 그러나 jar 자체에서 실행할 때 jar : file : /path/to/jar/jarname.jar! /file_in_jar.mp4와 유사한 URI를 얻습니다. jar로 시작하는 URI를 모든 것이 활용할 수있는 것은 아닙니다. 적절한 JavaFX 미디어의 경우.
Noah Ternullo

1
이 답변이 가장 좋습니다. 물론 대부분의 경우 jar 파일에있을 때 InputStream을 리소스로 가져가는 것이 바람직 할 수 있지만, 어떤 이유로 경로가 실제로 필요한 경우 이것이 작동합니다. 타사 객체를 제공 할 경로가 필요했습니다. 감사합니다!
마리오

1
IDE에있는 동안 위의 솔루션은 작업을하지 않는, Intellijit는 추가로 file:/항아리에 있지만 IDE에서 작동 경로에
Tayab 후세인

귀하의 답변으로 현재 최소 2 일 동안 해결하려는 문제가 해결되었습니다. TYVM !!
Jonathan

12

실제로 발견 된 솔루션이 이상하게도 효과가 없었기 때문에이 문제로 혼란스러워했습니다. 작업 디렉토리는 JAR의 디렉토리가 아닌 경우가 많으며, 특히 JAR (또는 해당 프로그램)이 Windows의 시작 메뉴에서 실행되는 경우에 특히 그렇습니다. 그래서 여기 내가 한 일이 있으며 JAR 외부에서도 실행되는 .class 파일에서도 작동합니다. (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

netclient.pJAR 파일 안에 있으면 해당 파일이 다른 파일 안에 있기 때문에 경로가 없습니다. 이 경우 가장 좋은 방법은 실제로 file:/path/to/jarfile/bot.jar!/config/netclient.p입니다.


이 형식 (... bot.jar! / config / ...)의 URL을 URI로 변환하려고하면 경로가 계층 적이 아니라고 말합니다.
gEdringer

7

jar 파일 내 경로를 이해해야합니다.
간단히 상대를 참조하십시오. 따라서 \src\main\resources디렉토리 (maven 스타일) 아래의 foo.jar에있는 파일 (myfile.txt)이있는 경우 . 당신은 그것을 다음과 같이 언급 할 것입니다 :

src/main/resources/myfile.txt

jar 파일을 사용하여 덤프 jar -tvf myjar.jar 하면 jar 파일 내의 출력과 상대 경로가 표시되고 FORWARD SLASHES가 사용됩니다.


실제로 Windows에서도 슬래시를 사용해야합니다. 이는 사용할 수 없음을 의미합니다 File.separator.
str

3

필자의 경우 경로 대신 URL 객체를 사용했습니다.

파일

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

클래스 로더를 사용하는 클래스 경로의 리소스

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

내용을 읽어야 할 때 다음 코드를 사용할 수 있습니다.

InputStream stream = url.openStream();

그리고 InputStream을 사용하여 컨텐츠에 액세스 할 수 있습니다.


3

이것은 jar 자원에서 불완전한 임시 파일 내용 복사를 피하고 고유 한 임시 파일 이름을 갖기 위해 스트림 플러시 및 닫기가있는 사용자 Tombart와 동일한 코드입니다.

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

File은 파일 시스템의 파일에 대한 추상화이며 파일 시스템은 JAR의 내용에 대해 아무것도 모릅니다.

URI로 시도해보십시오. 귀하의 지갑에 유용한 jar : // 프로토콜이 있다고 생각합니다.


1

다음 경로가 나를 위해 일했습니다. classpath:/path/to/resource/in/jar


1
@Anatoly 위에서 더 많은 데이터를 공유해 주
시겠습니까?

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

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

이것을 얻는 getSystemResourceAsStream것이 가장 좋은 방법입니다. 파일이나 URL이 아닌 입력 스트림을 가져 오면 JAR 파일에서 독립적으로 작동합니다.



0

jar 파일에있을 때 리소스는 파일 시스템 계층이 아닌 패키지 계층에 절대적으로 위치합니다. "./default.conf"라는 리소스를로드하는 com.example.Sweet 클래스가있는 경우 리소스 이름은 "/com/example/default.conf"로 지정됩니다.

그러나 그것이 항아리에 있다면 그것은 파일이 아닙니다 ...


0

당신의 능숙이 항아리의 (자바 / 주 / 자원) 폴더 안에 (우리는 당신이 xml 파일 이름을 추가 한 것으로 가정하여 파일을 추가 imports.xml을 당신이 주입 그 후,) ResourceLoader당신이 울부 짖는 소리처럼 스프링을 사용하는 경우

@Autowired
private ResourceLoader resourceLoader;

내부 둘러보기 기능은 파일을로드하기 위해 다음 코드를 작성합니다.

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

0

이 방법은 빠른 솔루션에 사용될 수 있습니다.

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

문맥을 생략하고 있습니까? java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Allen Luce

답을 편집하고 소프트웨어에서 사용한 전체 방법을 썼습니다
gbii

0

코드를 따르십시오!

/ 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;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.