jar 파일 내부에서 "폴더"리소스를 얻으려면 어떻게해야합니까?


139

프로젝트 루트에 리소스 폴더 / 패키지를 가지고 있는데 특정 파일을로드하고 싶지 않습니다. 특정 파일을로드하려면 class.getResourceAsStream을 사용하고 괜찮습니다! 내가 실제로하고 싶은 것은 resources 폴더 내에 "Folder"를로드하고 해당 폴더 안의 파일을 반복하여 각 파일에 스트림을 가져 와서 내용을 읽는 것입니다 ... 런타임 전에 파일 이름이 결정되지 않는다고 가정하십시오. ... 어떻게해야합니까? jar 파일의 폴더 안에있는 파일 목록을 얻는 방법이 있습니까? 리소스가있는 Jar 파일은 코드가 실행되는 것과 동일한 jar 파일입니다.

미리 감사드립니다 ...




5
첫 번째는 jar 파일을 추출하는 것입니다. 이것은 최악의 상황과 같은 최악의 상황과 같이 마지막으로 시도하고 싶은 것입니다! 파일을 추출하려면 설치시 파일을 설치 폴더에 넣으십시오! 항아리 내부에서 액세스하고 싶습니다. 내 이유는 getResourceAsStream이 파일에 액세스 할 수 있다면 Java는 추출 할 필요없이 해당 항아리의 폴더를 탐색 할 수 있어야합니다! 그러나 감사합니다 ...
Mostafa Zeinali

답변:


111

마지막으로 해결책을 찾았습니다.

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

두 번째 블록은 jar 파일이 아닌 IDE에서 응용 프로그램을 실행할 때 작동합니다. 좋지 않으면 제거 할 수 있습니다.


정답을 업데이트했습니다. 비록 이것이 34000+ 소스 파일을 가진 코드에서 내가하고 싶은 일이 아니지만 ...하지만 유일한 해결책 인 것 같습니다. 감사합니다.
Mostafa Zeinali

4
참고로, getPath는 서버 도메인과 관련된 것을 반환하기 때문에 webstart에서 시작하면 작동하지 않습니다.
amos

1
경로는 경우가 항아리에서 일할 거라고 생각하지 마십시오 것은, touri에 변환에이 오류를 줄 것이다 : java.lang.IllegalArgumentException가이 : URI는 계층 아니다
tribbloid

4
만 다른 항아리에 종속에서 단일 jar 파일에 대한 추출물 리소스에 작업을하지 않는 작동
tribbloid

2
이것은 다음과 같은 이유로 안전한 솔루션이 아닙니다. 1. .jar 파일이 동일한 시스템의 로컬 파일이라고 가정합니다. 2. URL.getPath ()는 유효한 파일 이름을 반환하지 않으며 URL의 경로 부분 만 반환하며 모든 퍼센트 이스케이프는 그대로 유지됩니다. 3. ProtectionDomain.getCodeSource ()는 null을 반환 할 수 있습니다 .
VGR

14

다음을 시도하십시오. 클래스 경로가 com.abc.package.MyClass이고
리소스 "<PathRelativeToThisClassFile>/<ResourceDirectory>"파일이 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
    }
}

당신은 또한 사용할 수 있습니다

URL url = MyClass.class.getResource("/com/abc/package/resources/");

27
시간과 노력에 감사드립니다. 그러나 코드 기반이 .jar 파일 내에 있고 리소스가 해당 .jar 파일에 있으면 작동하지 않습니다. .jar 파일 안에 있으면 File ()을 만들 수 없으며 해당 폴더의 URL도 얻을 수 없습니다 ... 개인적으로 그 폴더를 사용하여 각 파일을 XML로 나열했습니다 ... t jar 파일 안에있는 폴더에 대해 그렇게 할 수 있다고 생각합니다.
Mostafa Zeinali

2
문제가 발생하여 죄송합니다. 그러나 코드베이스가 jar 파일에 있고 리소스가 해당 jar 파일에있을 때 실제로 작동합니다. 디스크에 File ()을 작성하지 않고 jar 파일 내의 파일을 Java 메모리로 읽습니다. ClassLoader는 jar 파일을 디스크의 디렉토리와 동등한 것으로 취급하는 것을 매우 기쁘게 생각합니다. 출처는 다음과 같습니다.
Glen Best

1
항아리에서 코드를 실행하고 오류가 발생했습니다 ... 미안, 내 나쁜 "File : ...! / dir1 / dir2 ..."로 jar URL 경로를 처리해야합니다. 두 가지 수정 사항 : stackoverflow.com/questions/1429172/… OR String jarPath = dirURL.getPath (). substring (5, dirURL.getPath (). indexOf ( "!")); JarFile jar = 새로운 JarFile (URLDecoder.decode (jarPath, "UTF-8")); 열거 형 <JarEntry> 항목 = jar.entries (); while (entries.hasMoreElements ()) {// 무언가를하세요}
Glen Best

3
javadoc에서 메소드가 항상 모든 상황에서 파일을 리턴한다고 보장되는 것은 아닙니다. 그러나 실제로 Q를 읽는다면 OP는 100 % 파일 및 폴더 작업을 보장합니다. Q는 디렉토리와 파일에 대한 액세스를 요청합니다. 이는 API의 일반화 된 추상화보다 더 구체적입니다. 파일로의 변환은 필요에 맞게 적절하고 정확합니다. 이와 반대되는 의견을 게시하기 전에 Q를 제대로 읽으십시오. 건배.
Glen Best

7
OP가 파일 및 디렉토리와 작동하지 않습니다. 그는 JAR 파일의 자원으로 작업하고 있습니다. JAR 파일의 자원은 파일 또는 디렉토리가 아니며 File클래스 와 함께 나열 될 수 없습니다 . 이 코드는 JAR 파일 내부의 리소스에서는 작동하지 않습니다. 기간.
Lorne의 후작

7

나는 이것이 몇 년 전에 알고 있습니다. 그러나 다른 사람들에게만이 주제가 있습니다. 당신이 할 수있는 일은 getResourceAsStream()디렉토리 경로와 함께 메소드 를 사용 하는 것이며 입력 스트림은 해당 디렉토리의 모든 파일 이름을 갖습니다. 그런 다음 각 파일 이름으로 dir 경로를 연결하고 루프에서 각 파일에 대해 getResourceAsStream을 호출 할 수 있습니다.


7
이것은 문제가 발생하는 것을 계획하지 않는 한 잘 작동합니다.
Tustin2121

1
깨끗하고 별도의 경우 jar 및 IDE 버전을 처리 할 필요가 없으므로 이것이 허용되는 솔루션이어야한다고 생각합니다. 왜 getResource가 폴더를 찾지 않지만 getResourceAsStream은 폴더를 찾지 못하는지 잘 모르겠습니다.
Raghuram Onti Srinivasan

6

jar에 포장 된 리소스에서 일부 hadoop 구성을로드하려고 시도하는 동안 IDE와 jar (릴리스 버전)에서 동일한 문제가 발생했습니다.

java.nio.file.DirectoryStream로컬 파일 시스템과 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());
    }
}

이것은 나를 위해 잘 작동했습니다. 'Path jarFile = Paths.get (jar.toString (). substring ( "file :". length ()));'만 전환했습니다. '경로 jarFile = Paths.get (jar.toURI ());' Windows에서 작동하도록하십시오. FileSystem 오브젝트에 대한 try with resource 문도 사용되었습니다.
Jarle Jacobsen

이것은 나를 위해 일했다! docker에서 dropwizard 응용 프로그램을 실행 중이었습니다.
니콘 제

4

다음 코드는 원하는 "폴더"를 항아리 안에 있는지 여부에 관계없이 Path로 반환합니다.

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

Java 7 이상이 필요합니다.


좋은! 그러나 FileSystem은 닫을 수 있음을 추가해야합니다. 즉, 시스템 리소스를 확보하려면 어느 시점에서 닫아야합니다. 물론 반환 된 경로는 그 직후에 유효하지 않게됩니다.
Kirill Gamazkov

jar 파일의 경우 파일 시스템을 만들 때 리소스 이름 (여기에서 package + folder)이 무시되는 것으로 나타납니다. 따라서 getPath반환하지 않습니다 folder/path/to/...만, path/to/....
Marcono1234

1

또 다른 솔루션은 다음 ResourceLoader과 같이 사용할 수 있습니다 .

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())
}

0

단순 ... OSGi를 사용하십시오. OSGi에서 findEntries 및 findPaths를 사용하여 번들 항목을 반복 할 수 있습니다.


10
그것이 OSGI와 관련이 있다면 나는 그것을 '간단한'이라고 부르지 않을 것입니다. 그러나 이미 OSGI를 사용하고 있다면 확실히 그렇습니다. 그러나이 특정 문제를 해결하기 위해 OSGI로 옮기라고 제안하는 것은 너무 많은 것 같습니다.
Kris

OSGi는 또한 많은 다른 문제를 해결하며 현재는 시작하기가 매우 쉽습니다. OSGi enRoute 튜토리얼
Peter Kriens

OSGi를 사용하지 않으면 OSGi에서 멀리 떨어져 있습니다. 90 년대 IMO의 배포 문제를 해결하는 과도하게 설계된 Bloatware.
user2337270

-1

내 jar 파일 안에 Upload라는 폴더가 있었고이 폴더에는 3 개의 다른 텍스트 파일이 있었고 jar 파일 외부에 정확히 동일한 폴더와 파일이 있어야했습니다. 아래 코드를 사용했습니다.

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

1
안녕하세요, 답변 주셔서 감사하지만 코드를 작성할 때 해당 폴더 내의 각 파일 이름을 알아야합니다. 각각의 이름을 명시 적으로 지정해야했습니다. 내가 찾고있는 것은 폴더 내부의 파일을 반복하고 런타임에 파일 이름을 얻는 방법이므로 코드를 변경하지 않고 폴더에 리소스를 추가하여 코드도 처리한다는 것을 알았습니다. 그래도 시간 내 주셔서 감사합니다. : 3
Mostafa Zeinali

-1

다른 답변에서 지적했듯이 일단 리소스가 jar 파일 안에 있으면 문제가 발생합니다. 우리의 경우이 솔루션은 다음과 같습니다.

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

테스트에서 잘 작동하지만 (테스트가 실행될 때 코드가 jar 파일로 압축되어 있지 않기 때문에) 실제로 앱이 정상적으로 실행될 때는 작동하지 않습니다. 그래서 내가 한 일은 ... 앱에서 파일 목록을 하드 코딩하지만 디스크에서 실제 목록을 읽은 테스트가 있습니다 (테스트에서 작동하기 때문에 할 수 있음). 실제 목록이 그렇지 않으면 실패합니다 앱이 반환하는 목록과 일치하지 않습니다.

그런 식으로 내 응용 프로그램 (속임수)에 간단한 코드를, 그리고 나는 확실히 나는 시험에 대한 목록 덕분에 새 항목을 추가하는 것을 잊지 않았다거야.


-25

링크 는 방법을 알려줍니다.

마술은 getResourceAsStream () 메소드입니다.

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")

20
아니야!! 나는 하나의 파일을 원하지 않는다!! 하위 파일이 미리 결정되지 않은 전체 폴더를 원합니다 !!!
Mostafa Zeinali 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.