문제

디렉토리에서 모든 파일을 읽는 코드가 있습니다.

    File textFolder = new File("text_directory");

    File [] texFiles = textFolder.listFiles( new FileFilter() {
           public boolean accept( File file ) {
               return file.getName().endsWith(".txt");
           }
    });

잘 작동합니다."text_directory" 디렉터리에서 ".txt"로 끝나는 모든 파일로 배열을 채웁니다.

유사한 방식으로 디렉토리의 내용을 어떻게 읽을 수 있습니까? 이내에 JAR 파일?

그래서 제가 정말로 원하는 것은 JAR 파일 내의 모든 이미지를 나열하여 다음을 사용하여 로드하는 것입니다.

ImageIO.read(this.getClass().getResource("CompanyLogo.png"));

("CompanyLogo"가 "하드코드"되어 있기 때문에 작동하지만 JAR 파일 내부의 이미지 수는 가변 길이가 10~200일 수 있습니다.)

편집하다

그래서 내 주요 문제는 다음과 같습니다.아는 방법 JAR 파일 이름 내 메인 수업은 어디에 살고 있나요?

다음을 사용하여 읽을 수 있다고 인정했습니다. java.util.Zip.

내 구조는 다음과 같습니다

그들은 다음과 같습니다:

my.jar!/Main.class
my.jar!/Aux.class
my.jar!/Other.class
my.jar!/images/image01.png
my.jar!/images/image02a.png
my.jar!/images/imwge034.png
my.jar!/images/imagAe01q.png
my.jar!/META-INF/manifest 

지금은 다음을 사용하여 "images/image01.png" 예를 로드할 수 있습니다.

    ImageIO.read(this.getClass().getResource("images/image01.png));

하지만 파일 이름만 알고 있기 때문에 나머지는 동적으로 로드해야 합니다.

도움이 되었습니까?

해결책

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
  URL jar = src.getLocation();
  ZipInputStream zip = new ZipInputStream(jar.openStream());
  while(true) {
    ZipEntry e = zip.getNextEntry();
    if (e == null)
      break;
    String name = e.getName();
    if (name.startsWith("path/to/your/dir/")) {
      /* Do something with this entry. */
      ...
    }
  }
} 
else {
  /* Fail... */
}

Java 7에서는 다음을 생성할 수 있습니다. FileSystem JAR(zip) 파일에서 NIO의 디렉토리 탐색 및 필터링 메커니즘을 사용하여 검색합니다.이렇게 하면 JAR 및 "폭발된" 디렉터리를 처리하는 코드를 더 쉽게 작성할 수 있습니다.

다른 팁

IDE와 .jar 파일 모두에 맞는 코드 :

import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;

public class ResourceWalker {
    public static void main(String[] args) throws URISyntaxException, IOException {
        URI uri = ResourceWalker.class.getResource("/resources").toURI();
        Path myPath;
        if (uri.getScheme().equals("jar")) {
            FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
            myPath = fileSystem.getPath("/resources");
        } else {
            myPath = Paths.get(uri);
        }
        Stream<Path> walk = Files.walk(myPath, 1);
        for (Iterator<Path> it = walk.iterator(); it.hasNext();){
            System.out.println(it.next());
        }
    }
}

에릭슨 대답 완벽하게 일했습니다 :

작동 코드는 다음과 같습니다.

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();

if( src != null ) {
    URL jar = src.getLocation();
    ZipInputStream zip = new ZipInputStream( jar.openStream());
    ZipEntry ze = null;

    while( ( ze = zip.getNextEntry() ) != null ) {
        String entryName = ze.getName();
        if( entryName.startsWith("images") &&  entryName.endsWith(".png") ) {
            list.add( entryName  );
        }
    }

 }
 webimages = list.toArray( new String[ list.size() ] );

그리고 나는 이것에서 내로드 방법을 수정했습니다.

File[] webimages = ... 
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));

이에:

String  [] webimages = ...

BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));

acheron55를 확장하고 싶습니다. 답변, 여러 가지 이유로 인해 매우 안전하지 않은 솔루션이기 때문입니다.

  1. 닫히지는 않아요 FileSystem 물체.
  2. 여부는 확인하지 않습니다. FileSystem 개체가 이미 존재합니다.
  3. 스레드로부터 안전하지 않습니다.

이것은 다소 안전한 솔루션입니다.

private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();

public void walk(String path) throws Exception {

    URI uri = getClass().getResource(path).toURI();
    if ("jar".equals(uri.getScheme()) {
        safeWalkJar(path, uri);
    } else {
        Files.walk(Paths.get(path));
    }
}

private void safeWalkJar(String path, URI uri) throws Exception {

    synchronized (getLock(uri)) {    
        // this'll close the FileSystem object at the end
        try (FileSystem fs = getFileSystem(uri)) {
            Files.walk(fs.getPath(path));
        }
    }
}

private Object getLock(URI uri) {

    String fileName = parseFileName(uri);  
    locks.computeIfAbsent(fileName, s -> new Object());
    return locks.get(fileName);
}

private String parseFileName(URI uri) {

    String schemeSpecificPart = uri.getSchemeSpecificPart();
    return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!"));
}

private FileSystem getFileSystem(URI uri) throws IOException {

    try {
        return FileSystems.getFileSystem(uri);
    } catch (FileSystemNotFoundException e) {
        return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap());
    }
}   

파일 이름에 대해 실제로 동기화할 필요는 없습니다.매번 동일한 객체에 대해 간단히 동기화할 수 있습니다(또는 메소드를 만들 수 있습니다). synchronized), 이는 순전히 최적화입니다.

코드에 다음을 사용하는 다른 부분이 있을 수 있기 때문에 이것이 여전히 문제가 있는 해결책이라고 말하고 싶습니다. FileSystem 동일한 파일에 대한 인터페이스를 수행하므로 해당 파일을 방해할 수 있습니다(단일 스레드 응용 프로그램에서도).
또한, 확인하지 않습니다. null(예를 들어, getClass().getResource().

이 특정 Java NIO 인터페이스는 스레드로부터 안전하지 않은 전역/싱글톤 리소스를 도입하고 문서가 매우 모호하기 때문에 다소 끔찍합니다(제공자별 구현으로 인해 알 수 없는 것이 많습니다).다른 경우에는 결과가 다를 수 있습니다. FileSystem 공급자(JAR 아님).아마도 그렇게 되는 데에는 그럴 만한 이유가 있을 것입니다.구현을 연구하지 않았습니다.

다음은 "패키지 아래에서 모든 주니를 실행"하는 방법입니다. 당신은 당신의 요구에 그것을 조정할 수 있어야합니다.

private static void findClassesInJar(List<String> classFiles, String path) throws IOException {
    final String[] parts = path.split("\\Q.jar\\\\E");
    if (parts.length == 2) {
        String jarFilename = parts[0] + ".jar";
        String relativePath = parts[1].replace(File.separatorChar, '/');
        JarFile jarFile = new JarFile(jarFilename);
        final Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            final String entryName = entry.getName();
            if (entryName.startsWith(relativePath)) {
                classFiles.add(entryName.replace('/', File.separatorChar));
            }
        }
    }
}

편집 : 아,이 경우, 당신은이 스 니펫을 원할 수도 있습니다 (동일한 유스 케이스 :))

private static File findClassesDir(Class<?> clazz) {
    try {
        String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
        final String codeSourcePath = URLDecoder.decode(path, "UTF-8");
        final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce('.', File.separatorChar));
    } catch (UnsupportedEncodingException e) {
        throw new AssertionError("impossible", e);
    }
}

그래서 나는 나의 주요 문제가 나의 주요 계급이 사는 항아리의 이름을 아는 방법이다.

프로젝트가 항아리에 포장되어 있다고 가정하면 (반드시 사실이 아닙니다!) ClassLoader.getResource () 또는 FindResource ()를 사용하여 클래스 이름 (.class)을 사용하여 주어진 클래스가 포함 된 항아리를 얻을 수 있습니다. 당신은 반환되는 URL에서 항아리 이름을 구문 분석해야합니다.

클래스가 항아리의 일부가 아닌 경우를 테스트하십시오.

JAR 파일은 구조화 된 매니페스트가있는 ZIP 파일 일뿐입니다. 일반적인 Java Zip 도구로 JAR 파일을 열고 파일 내용을 그런 식으로 스캔하고 스트림 등을 늘릴 수 있습니다. 그런 다음 getResourceasStream 호출에 사용하면 모두 HUMKY DORY 여야합니다.

편집 / 설명 후

모든 비트와 조각을 기억하는 데 1 분이 걸렸고 더 깨끗한 방법이 있다고 확신하지만, 나는 미쳤다는 것을보고 싶었습니다. 내 프로젝트에서 image.jpg는 기본 JAR 파일의 일부에있는 파일입니다. 메인 클래스의 클래스 로더 (someclass는 진입 점)를 얻고이를 사용하여 image.jpg 리소스를 발견합니다. 그런 다음 일부 스트림 마법 으로이 ImageInputStream에 들어가면 모든 것이 정상입니다.

InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg");
JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi();
ImageReader ir = imageReaderSpi.createReaderInstance();
ImageInputStream iis = new MemoryCacheImageInputStream(inputStream);
ir.setInput(iis);
....
ir.read(0); //will hand us a buffered image

실제 JAR 파일이 주어지면 다음을 사용하여 내용을 나열할 수 있습니다. JarFile.entries().하지만 JAR 파일의 위치를 ​​알아야 합니다. 클래스 로더에 얻을 수 있는 모든 항목을 나열하도록 요청할 수는 없습니다.

반환된 URL을 기반으로 JAR 파일의 위치를 ​​알아낼 수 있어야 합니다. ThisClassName.class.getResource("ThisClassName.class"), 하지만 조금 까다로울 수도 있습니다.

얼마 전에 나는 내부 항아리에서 분류하는 기능을 만들었습니다.

public static Class[] getClasses(String packageName) 
throws ClassNotFoundException{
    ArrayList<Class> classes = new ArrayList<Class> ();

    packageName = packageName.replaceAll("\\." , "/");
    File f = new File(jarName);
    if(f.exists()){
        try{
            JarInputStream jarFile = new JarInputStream(
                    new FileInputStream (jarName));
            JarEntry jarEntry;

            while(true) {
                jarEntry=jarFile.getNextJarEntry ();
                if(jarEntry == null){
                    break;
                }
                if((jarEntry.getName ().startsWith (packageName)) &&
                        (jarEntry.getName ().endsWith (".class")) ) {
                    classes.add(Class.forName(jarEntry.getName().
                            replaceAll("/", "\\.").
                            substring(0, jarEntry.getName().length() - 6)));
                }
            }
        }
        catch( Exception e){
            e.printStackTrace ();
        }
        Class[] classesA = new Class[classes.size()];
        classes.toArray(classesA);
        return classesA;
    }else
        return null;
}

다음은 사용의 예입니다 반사 라이브러리는 재귀 적으로 클래스 경로를 재귀 적으로 스캔하여 몇 개의 구아바 리소스 내용을 가져 오는 특전 :

Reflections reflections = new Reflections("com.example.package", new ResourcesScanner());
Set<String> paths = reflections.getResources(Pattern.compile(".*\\.template$"));

Map<String, String> templates = new LinkedHashMap<>();
for (String path : paths) {
    log.info("Found " + path);
    String templateName = Files.getNameWithoutExtension(path);
    URL resource = getClass().getClassLoader().getResource(path);
    String text = Resources.toString(resource, StandardCharsets.UTF_8);
    templates.put(templateName, text);
}

이것은 항아리와 폭발 수업과 함께 작동합니다.

나는 포팅했다 Acheron55의 답변 Java 7에 FileSystem 물체. 이 코드는 IDE, JAR 파일 및 Tomcat 7의 전쟁 안에서 항아리에서 작동합니다. 그러나 그것은 그렇습니다 ~ 아니다 Jboss 7의 전쟁 안에서 항아리에서 일하는 FileSystemNotFoundException: Provider "vfs" not installed, 또한보십시오 이 게시물). 더욱 errr. 이러한 이유로 나는이 해결책을 버렸다. 그러나 이러한 문제를 수락 할 수 있다면 기성품 코드는 다음과 같습니다.

import java.io.IOException;
import java.net.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;

public class ResourceWalker {

    public static void main(String[] args) throws URISyntaxException, IOException {
        URI uri = ResourceWalker.class.getResource("/resources").toURI();
        System.out.println("Starting from: " + uri);
        try (FileSystem fileSystem = (uri.getScheme().equals("jar") ? FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap()) : null)) {
            Path myPath = Paths.get(uri);
            Files.walkFileTree(myPath, new SimpleFileVisitor<Path>() { 
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    System.out.println(file);
                    return FileVisitResult.CONTINUE;
                }
            });
        }
    }
}

Jarscan이라는 두 가지 유용한 유틸리티가 있습니다.

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

이 질문도 참조하십시오. Jarscan, 특정 클래스의 모든 하위 폴더의 모든 JAR 파일을 스캔하십시오.

항아리 URL에서 파일을 목록/읽는 다른 방법으로 중첩 된 항아리를 재귀 적으로 수행합니다.

https://gist.github.com/trung/2cd90faab7f75b3bcbaa

URL urlResource = Thead.currentThread().getContextClassLoader().getResource("foo");
JarReader.read(urlResource, new InputStreamCallback() {
    @Override
    public void onFile(String name, InputStream is) throws IOException {
        // got file name and content stream 
    }
});
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top