sourcecode

JAR 파일 내의 파일을 나열하려면 어떻게 해야 합니까?

copyscript 2022. 9. 23. 00:05
반응형

JAR 파일 내의 파일을 나열하려면 어떻게 해야 합니까?

나는 디렉토리의 모든 파일을 읽는 코드를 가지고 있다.

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

(「Company Logo」는 「하드 코드」이기 때문에 동작합니다만, JAR 파일내의 이미지의 수는 10 ~200 의 가변 길이입니다).

EDIT

그래서 제 주된 문제는 다음과 같습니다.메인 클래스가 있는 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... */
}

7에서는 7을 할 수 .FileSystemJAR(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예를 s('s'가 켜짐)getClass().getResource().

이 특정 Java NIO 인터페이스는 글로벌/싱글톤 비스레드 세이프 리소스를 도입하고 문서화가 매우 모호하기 때문에 좀 끔찍합니다(프로바이더 고유의 구현으로 인해 알 수 없는 경우가 많습니다).는 그 외의 경우에 수 .FileSystemjar바외제 maybejar외외 maybe maybe maybe maybe maybe for for for for for for for i그럴 만한 이유가 있을지도 모릅니다.실현에 대해서는 아직 조사하지 않았습니다.

그래서 내 주된 문제는 우리 반 아이들이 살고 있는 항아리의 이름을 어떻게 아는가 하는 것 같아.

프로젝트가 Jar(반드시 true는 아님)에 포함되어 있다고 가정하면 ClassLoader.getResource() 또는 findResource()와 클래스 이름(.class 뒤에 있음)을 사용하여 지정된 클래스를 포함하는 jar를 가져올 수 있습니다.반환되는 URL에서 jar name을 해석해야 합니다(그렇게 어려운 것은 아닙니다).이거는 독자를 위한 연습으로 남겨두겠습니다:-)

클래스가 항아리에 포함되지 않은 경우 반드시 테스트하십시오.

acheron55의 답변을 Java 7에 포팅하고FileSystem물건.이 코드는 Tomcat 7의 전쟁 내 IDE, jar 파일 및 jar에서 작동하지만 JBoss 7의 전쟁 내 jar에서는 작동하지 않습니다.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;
                }
            });
        }
    }
}

다음은 Reflections 라이브러리를 사용하여 리소스 내용을 가져오는 몇 가지 Guava 권한으로 증강된 regex 이름 패턴을 통해 클래스 경로를 재귀적으로 검색하는 예제입니다.

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

이것은 항아리와 폭발 클래스 모두에서 작동합니다.

"모든 JUnits를 패키지로 실행"하기 위해 작성한 방법이 있습니다.당신은 그것을 당신의 필요에 맞게 조정할 수 있어야 합니다.

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

jar 파일은 구조화된 매니페스토를 가진 zip 파일입니다.일반적인 Java zip 도구로 jar 파일을 열고 파일 내용을 스캔하거나 스트림을 부풀리는 등의 방법으로 실행할 수 있습니다.그런 다음 getResourceAsStream 호출에서 사용하면 모든 것이 정상입니다.

편집 / 설명 후

모든 조각들을 기억하는 데 1분이 걸렸고 더 나은 방법이 있을 거라고 확신하지만, 나는 내가 미쳤지 않은지 보고 싶었다.프로젝트 이미지에서.jpg는 메인 jar 파일의 일부에 있는 파일입니다.메인 클래스의 클래스 로더(SomeClass는 엔트리 포인트)를 가져와 image.jpg 리소스를 검출하기 위해 사용합니다.그런 다음 이 이미지에 삽입하기 위한 스트리밍 마법을 사용합니다.Input Stream과 관련된 모든 것이 정상입니다.

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")조금 흔들릴 수도 있습니다.

얼마 전에 JAR 내부에서 클래스리스 기능을 만들었습니다.

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;
}
public static ArrayList<String> listItems(String path) throws Exception{
    InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
    byte[] b = new byte[in.available()];
    in.read(b);
    String data = new String(b);
    String[] s = data.split("\n");
    List<String> a = Arrays.asList(s);
    ArrayList<String> m = new ArrayList<>(a);
    return m;
}

참고로 이미 Spring을 사용하고 있다면PathMatchingResourcePatternResolver.

예를 들어, 모든 PNG 파일을images in resources 내 폴더

ClassLoader cl = this.getClass().getClassLoader(); 
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
Resource[] resources = resolver.getResources("images/*.png");
for (Resource r: resources){
    logger.info(r.getFilename());
    // From your example
    // ImageIO.read(cl.getResource("images/" + r.getFilename()));
}

JarScan이라고 불리는 두 가지 매우 유용한 유틸리티가 있습니다.

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

다음 질문도 참조하십시오.JarScan, 모든 하위 폴더에 있는 모든 JAR 파일에서 특정 클래스를 검색합니다.

ClassPath 내의 모든 리소스를 나열하는 가장 강력한 메커니즘은 현재 ClassGraph에서 이 패턴을 사용하는 입니다.이는 새로운 JPMS 모듈 시스템을 포함하여 가능한 한 광범위한 클래스 경로 지정 메커니즘을 처리하기 때문입니다.(Class Graph의 저자입니다.)

메인 클래스가 있는 JAR 파일의 이름을 어떻게 알 수 있습니까?

URI mainClasspathElementURI;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
        .enableClassInfo().scan()) {
    mainClasspathElementURI =
            scanResult.getClassInfo("x.y.z.MainClass").getClasspathElementURI();
}

JAR 파일 내에서도 같은 방법으로 디렉토리의 내용을 읽는 방법은 무엇입니까?

List<String> classpathElementResourcePaths;
try (ScanResult scanResult = new ClassGraph().overrideClasspath(mainClasspathElementURI)
        .scan()) {
    classpathElementResourcePaths = scanResult.getAllResources().getPaths();
}

자원을 다루는 방법은 그 에도 많이 있습니다.

와일드카드글로빙을 사용하기 때문에 특정 파일명을 매칭하기 위해 좀 더 유연한 도로용입니다.기능적 스타일은 다음과 같습니다.

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;

import static java.nio.file.FileSystems.getDefault;
import static java.nio.file.FileSystems.newFileSystem;
import static java.util.Collections.emptyMap;

/**
 * Responsible for finding file resources.
 */
public class ResourceWalker {
  /**
   * Globbing pattern to match font names.
   */
  public static final String GLOB_FONTS = "**.{ttf,otf}";

  /**
   * @param directory The root directory to scan for files matching the glob.
   * @param c         The consumer function to call for each matching path
   *                  found.
   * @throws URISyntaxException Could not convert the resource to a URI.
   * @throws IOException        Could not walk the tree.
   */
  public static void walk(
    final String directory, final String glob, final Consumer<Path> c )
    throws URISyntaxException, IOException {
    final var resource = ResourceWalker.class.getResource( directory );
    final var matcher = getDefault().getPathMatcher( "glob:" + glob );

    if( resource != null ) {
      final var uri = resource.toURI();
      final Path path;
      FileSystem fs = null;

      if( "jar".equals( uri.getScheme() ) ) {
        fs = newFileSystem( uri, emptyMap() );
        path = fs.getPath( directory );
      }
      else {
        path = Paths.get( uri );
      }

      try( final var walk = Files.walk( path, 10 ) ) {
        for( final var it = walk.iterator(); it.hasNext(); ) {
          final Path p = it.next();
          if( matcher.matches( p ) ) {
            c.accept( p );
          }
        }
      } finally {
        if( fs != null ) { fs.close(); }
      }
    }
  }
}

파일 확장자를 파라미터화하는 것을 검토해 주십시오.독자를 위한 연습은 남겨두겠습니다.

하세요.Files.walk ★★★★★★★★★★★★★★★★★★★:

이 메서드는 스트림의 작업이 완료된 후 스트림의 열린 디렉토리가 즉시 닫히도록 하려면 try-with-resources 문 또는 유사한 제어 구조 내에서 사용해야 합니다.

저저마마 likewise likewise likewise likewise likewise likewise likewise likewise.newFileSystem워커가 파일 시스템 경로를 방문하기 전에는 닫아야 합니다.

jar URL에서 파일을 나열/읽는 다른 방법으로 네스트된 jar에 대해 재귀적으로 실행

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

언급URL : https://stackoverflow.com/questions/1429172/how-to-list-the-files-inside-a-jar-file

반응형