Làm cách nào để liệt kê các tệp bên trong tệp JAR?


114

Tôi có mã này đọc tất cả các tệp từ một thư mục.

    File textFolder = new File("text_directory");

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

Nó hoạt động tuyệt vời. Nó lấp đầy mảng với tất cả các tệp kết thúc bằng ".txt" từ thư mục "text_directory".

Làm cách nào tôi có thể đọc nội dung của một thư mục theo cách tương tự trong tệp JAR?

Vì vậy, điều tôi thực sự muốn làm là liệt kê tất cả các hình ảnh bên trong tệp JAR của mình, để tôi có thể tải chúng bằng:

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

(Cái đó hoạt động vì "CompanyLogo" là "mã cứng" nhưng số lượng hình ảnh bên trong tệp JAR có thể có độ dài thay đổi từ 10 đến 200.)

BIÊN TẬP

Vì vậy, tôi đoán vấn đề chính của tôi sẽ là: Làm thế nào để biết tên của tệp JAR nơi lớp chính của tôi sống?

Tôi có thể đọc nó bằng cách sử dụng java.util.Zip.

Cấu trúc của tôi như thế này:

Họ là như thế:

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 

Ngay bây giờ tôi có thể tải ví dụ: "images / image01.png" bằng cách sử dụng:

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

Nhưng chỉ vì tôi biết tên tệp, phần còn lại tôi phải tải chúng động.


Chỉ là một suy nghĩ - tại sao không nén hình ảnh / jar vào một tệp riêng biệt và đọc các mục nhập trong đó từ lớp của bạn trong một jar khác?
Vineet Reynolds

3
Bởi vì nó sẽ cần một bước "bổ sung" để phân phối / cài đặt. :( Bạn biết đấy, người dùng cuối.
OscarRyz 15/09/09

Cho rằng bạn đã tạo jar, bạn cũng có thể bao gồm danh sách các tệp bên trong nó thay vì thử bất kỳ thủ thuật nào.
Tom Hawtin - tackline

Chà, tôi có thể nhầm nhưng lọ có thể được nhúng vào bên trong lọ khác. Giải pháp đóng gói một lọ (TM) ibm.com/developerworks/java/library/j-onejar hoạt động trên cơ sở này. Ngoại trừ, trong trường hợp của bạn, bạn không yêu cầu các lớp tải khả năng.
Vineet Reynolds 15/09/09

Câu trả lời:


91
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... */
}

Lưu ý rằng trong Java 7, bạn có thể tạo một FileSystemtừ tệp JAR (zip), sau đó sử dụng cơ chế lọc và đi bộ thư mục của NIO để tìm kiếm trong đó. Điều này sẽ giúp viết mã xử lý các JAR và các thư mục "bùng nổ" dễ dàng hơn.


này, cảm ơn ... đã tìm cách để làm điều này trong vài giờ nay !!
Newtopian

9
Có mã này hoạt động nếu chúng tôi muốn liệt kê tất cả các mục nhập bên trong tệp jar này. Nhưng nếu tôi chỉ muốn liệt kê một thư mục con bên trong jar, chẳng hạn như example.jar / dir1 / dir2 / , thì làm cách nào để tôi có thể liệt kê trực tiếp tất cả các tệp bên trong thư mục con này? Hay tôi cần giải nén tệp jar này? Tôi đánh giá cao sự giúp đỡ của bạn!
Ensom Hodder

Cách tiếp cận Java 7 đã đề cập được liệt kê trong câu trả lời của @ acheron55 .
Vadzim

@Vadzim Bạn có chắc câu trả lời của acheron55 dành cho Java 7 không? Tôi không tìm thấy Files.walk () hoặc java.util.Stream trong Java 7, nhưng trong Java 8: docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html
Bruce Chủ nhật

@BruceSun, trong java 7, bạn có thể sử dụng Files.walkFileTree (...) để thay thế.
Vadzim

80

Mã hoạt động cho cả tệp IDE và .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());
        }
    }
}

5
FileSystems.newFileSystem()lấy a Map<String, ?>, vì vậy bạn cần xác định Collections.emptyMap()rằng nó cần trả về một giá trị được đánh thích hợp. Đây hoạt động: Collections.<String, Object>emptyMap().
Zero3

6
Tuyệt diệu!!! nhưng URI uri = MyClass.class.getResource ("/ resources"). toURI (); nên có MyClass.class.getClassLoader (). getResource ("/ resources"). toURI (); tức là, getClassLoader (). Nếu không nó đã không hoạt động cho tôi.
EMM

8
Đừng quên đóng cửa fileSystem!
gmjonker

3
Đây phải là câu trả lời đầu tiên cho 1.8 ( walkmethod in Fileschỉ có sẵn trong 1.8). Vấn đề duy nhất là thư mục tài nguyên xuất hiện trong Files.walk(myPath, 1), không chỉ các tệp. Tôi đoán các yếu tố đầu tiên có thể được chỉ đơn giản là bỏ qua
toto_tico

4
myPath = fileSystem.getPath("/resources");không làm việc cho tôi; nó không tìm thấy gì cả. Nó phải là "hình ảnh" trong trường hợp của tôi và thư mục "hình ảnh" chắc chắn được bao gồm trong lọ của tôi!
phip1611, 31/03/18

21

Câu trả lời của erickson hoạt động hoàn hảo:

Đây là mã làm việc.

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

Và tôi vừa sửa đổi phương thức tải của mình từ điều này:

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

Về điều này:

String  [] webimages = ...

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

9

Tôi muốn mở rộng câu trả lời của acheron55 , vì nó là một giải pháp rất không an toàn, vì một số lý do:

  1. Nó không đóng FileSystemđối tượng.
  2. Nó không kiểm tra xem FileSystemđối tượng đã tồn tại hay chưa.
  3. Nó không an toàn theo luồng.

Đây là một giải pháp an toàn hơn:

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

Không cần thực sự đồng bộ hóa qua tên tệp; người ta có thể đơn giản đồng bộ hóa trên cùng một đối tượng mọi lúc (hoặc tạo phương thức synchronized), đó hoàn toàn là một sự tối ưu hóa.

Tôi muốn nói rằng đây vẫn là một giải pháp có vấn đề, vì có thể có các phần khác trong mã sử dụng FileSystemgiao diện trên các tệp giống nhau và nó có thể can thiệp vào chúng (ngay cả trong một ứng dụng luồng đơn).
Ngoài ra, nó không kiểm tra nulls (ví dụ: bật getClass().getResource().

Giao diện Java NIO cụ thể này khá khủng khiếp, vì nó giới thiệu một tài nguyên toàn cầu / singleton không an toàn theo luồng và tài liệu của nó cực kỳ mơ hồ (rất nhiều ẩn số do các triển khai cụ thể của nhà cung cấp). Kết quả có thể khác nhau đối với các FileSystemnhà cung cấp khác (không phải JAR). Có lẽ có một lý do chính đáng cho việc đó là như vậy; Tôi không biết, tôi chưa nghiên cứu việc triển khai.


1
Đồng bộ hóa các tài nguyên bên ngoài, như FS, không có nhiều ý nghĩa trong một máy ảo. Có thể có các ứng dụng khác truy cập nó bên ngoài máy ảo của bạn. Bên cạnh đó, ngay cả bên trong ứng dụng của bạn, việc khóa dựa trên tên tệp của bạn có thể dễ dàng bị bỏ qua. Với những điều này, tốt hơn là dựa vào cơ chế đồng bộ hóa hệ điều hành, như khóa tệp.
Espinosa

@Espinosa Cơ chế khóa tên tệp hoàn toàn có thể bị bỏ qua; câu trả lời của tôi cũng không đủ an toàn, nhưng tôi tin rằng đó là những gì bạn có thể nhận được với Java NIO với nỗ lực tối thiểu. Dựa vào hệ điều hành để quản lý khóa hoặc không kiểm soát ứng dụng nào truy cập vào tệp nào là một IMHO thực tế không tốt, trừ khi bạn đang xây dựng một ứng dụng dựa trên giá tiền - ví dụ, một trình soạn thảo văn bản. Việc không tự quản lý các khóa sẽ khiến các ngoại lệ được đưa ra hoặc khiến các luồng chặn ứng dụng - cả hai đều nên tránh.
Eyal Roth

8

Vì vậy, tôi đoán vấn đề chính của tôi sẽ là, làm thế nào để biết tên của cái lọ nơi lớp chính của tôi sống.

Giả sử rằng dự án của bạn được đóng gói trong một Jar (không nhất thiết phải đúng!), Bạn có thể sử dụng ClassLoader.getResource () hoặc findResource () với tên lớp (theo sau là .class) để lấy jar chứa một lớp nhất định. Bạn sẽ phải phân tích cú pháp tên jar từ URL được trả về (không khó lắm), tôi sẽ để lại như một bài tập cho người đọc :-)

Đảm bảo kiểm tra trường hợp lớp không phải là một phần của bình.


1
huh - thú vị là điều này sẽ được sửa đổi mà không cần bình luận ... Chúng tôi sử dụng kỹ thuật trên mọi lúc và nó hoạt động tốt.
Ngày Kevin

Một vấn đề cũ, nhưng với tôi đây có vẻ như là một vụ hack tốt. Số phiếu ủng hộ trở lại con số 0 :)
Tuukka Mustonen

Được ủng hộ, bởi vì đây là giải pháp duy nhất được liệt kê ở đây cho trường hợp khi một lớp không có CodeSource.
Phục hồi Monica 2331977

7

Tôi đã chuyển câu trả lời của acheron55 sang Java 7 và đóng FileSystemđối tượng. Mã này hoạt động trong IDE, trong các tệp jar và trong một cái lọ bên trong cuộc chiến trên Tomcat 7; nhưng lưu ý rằng nó không hoạt động trong một cái lọ bên trong cuộc chiến trên JBoss 7 (nó mang lại FileSystemNotFoundException: Provider "vfs" not installed, xem thêm bài đăng này ). Hơn nữa, giống như mã gốc, nó không an toàn cho luồng, như đề xuất của errr . Vì những lý do này, tôi đã từ bỏ giải pháp này; tuy nhiên, nếu bạn có thể chấp nhận những vấn đề này, đây là mã tạo sẵn của tôi:

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

5

Đây là một phương pháp tôi đã viết để "chạy tất cả các JUnits trong một gói". Bạn sẽ có thể điều chỉnh nó theo nhu cầu của mình.

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

Chỉnh sửa: À, trong trường hợp đó, bạn cũng có thể muốn đoạn mã này (cùng một trường hợp sử dụng :))

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

1
Tôi đoán vấn đề lớn là phải biết tên tệp jar ngay từ đầu. Nó là cái lọ nơi Main-Class: sống.
OscarRyz

5

Dưới đây là một ví dụ về việc sử dụng thư viện Reflections để quét đệ quy classpath theo mẫu tên regex được tăng cường với một vài đặc quyền của Guava để tìm nạp nội dung tài nguyên:

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

Điều này hoạt động với cả bình và các lớp phát nổ.


Hãy lưu ý rằng phản xạ vẫn không hỗ trợ Java 9 trở lên: github.com/ronmamo/reflections/issues/186 . Có các liên kết đến các thư viện cạnh tranh ở đó.
Vadzim

3

Tệp jar chỉ là tệp zip có tệp kê khai có cấu trúc. Bạn có thể mở tệp jar bằng các công cụ java zip thông thường và quét nội dung tệp theo cách đó, thổi phồng luồng, v.v. Sau đó, sử dụng tệp đó trong lệnh gọi getResourceAsStream và tất cả phải là hunky dory.

CHỈNH SỬA / sau khi làm rõ

Tôi mất một phút để nhớ tất cả các mảnh và mảnh ghép và tôi chắc chắn rằng có nhiều cách tốt hơn để làm điều đó, nhưng tôi muốn thấy rằng tôi không điên. Trong dự án của tôi, image.jpg là một tệp nằm trong một số phần của tệp jar chính. Tôi lấy trình tải lớp của lớp chính (SomeClass là điểm nhập) và sử dụng nó để khám phá tài nguyên image.jpg. Sau đó, một số phép thuật phát trực tiếp để đưa nó vào thứ ImageInputStream này và mọi thứ đều ổn.

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

Bình này chứa chương trình chính và các tài nguyên. Làm thế nào để tôi tham khảo bình tự? từ trong tệp jar?
OscarRyz

Để tham chiếu đến tệp JAR, chỉ cần sử dụng "blah.JAR" làm Chuỗi. Bạn có thể sử dụng new File("blah.JAR")để tạo một đối tượng Tệp đại diện cho JAR chẳng hạn. Chỉ cần thay thế "blah.JAR" bằng tên JAR của bạn.
Thomas Owens

Nếu nó là cùng một jar mà bạn đã sử dụng hết, trình tải lớp sẽ có thể nhìn thấy những thứ bên trong jar ... Tôi đã hiểu sai những gì bạn đang cố gắng làm ban đầu.
Mikeb 15/09/09

2
Vâng, tôi đã có sẵn điều đó, vấn đề là khi tôi cần một cái gì đó như: "... getResourceAsStream (" *. Jpg "); ..." Đó là, động, liệt kê các tệp được chứa.
OscarRyz

3

Với một tệp JAR thực tế, bạn có thể liệt kê nội dung bằng cách sử dụng JarFile.entries(). Mặc dù vậy, bạn sẽ cần biết vị trí của tệp JAR - bạn không thể chỉ yêu cầu trình tải lớp liệt kê mọi thứ mà nó có thể nhận được.

Bạn sẽ có thể tìm ra vị trí của tệp JAR dựa trên URL được trả về từ đó ThisClassName.class.getResource("ThisClassName.class"), nhưng nó có thể hơi khó hiểu.


Đọc câu trả lời của bạn một câu hỏi khác được nêu ra. Điều gì sẽ mang lại lời gọi: this.getClass (). GetResource ("/ my_directory"); Nó sẽ trả về một URL có thể được .... sử dụng làm thư mục? Nahh ... để tôi thử xem.
OscarRyz

Bạn luôn biết vị trí của JAR - nó nằm trong ".". Miễn là tên của JAR được biết đến là một cái gì đó, bạn có thể sử dụng một hằng chuỗi ở đâu đó. Bây giờ, nếu mọi người thay đổi tên của JAR ...
Thomas Owens 15/09/09

@Thomas: Giả sử bạn đang chạy ứng dụng từ thư mục hiện tại. Có gì sai với "java -jar foo / bar / baz.jar"?
Jon Skeet

Tôi tin rằng (và sẽ phải xác minh), rằng nếu bạn có mã trong Jar new File("baz.jar), thì đối tượng File sẽ đại diện cho tệp JAR của bạn.
Thomas Owens

@Thomas: Tôi không tin như vậy. Tôi tin rằng nó sẽ liên quan đến thư mục làm việc hiện tại của quy trình. Mặc dù vậy tôi cũng phải kiểm tra :)
Jon Skeet 15/09/09

3

Cách đây một thời gian, tôi đã tạo một hàm nhận classess từ bên trong 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;
}

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

3
Mặc dù đoạn mã này có thể giải quyết vấn đề, nhưng nó không giải thích tại sao hoặc cách nó trả lời câu hỏi. Vui lòng bao gồm giải thích cho mã của bạn , vì điều đó thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Samuel Philipp

dữ liệu trống khi chúng tôi thực thi mã từ tệp jar.
Aguid


1

Cơ chế mạnh mẽ nhất để liệt kê tất cả các tài nguyên trong classpath hiện là sử dụng mẫu này với ClassGraph , vì nó xử lý mảng cơ chế đặc tả classpath rộng nhất có thể , bao gồm cả hệ thống mô-đun JPMS mới. (Tôi là tác giả của ClassGraph.)

Làm thế nào để biết tên của tệp JAR nơi lớp chính của tôi sống?

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

Làm cách nào tôi có thể đọc nội dung của một thư mục theo cách tương tự trong tệp JAR?

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

rất nhiều cách khác để xử lý tài nguyên .


1
Gói rất đẹp, dễ dàng sử dụng trong dự án Scala của tôi, cảm ơn bạn.
zslim

0

Chỉ là một cách khác để liệt kê / đọc tệp từ URL jar và nó thực hiện đệ quy cho các lọ lồng nhau

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

0

Một nữa cho con đường:

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;

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

public class ResourceWalker {
  private static final PathMatcher FILE_MATCHER =
      FileSystems.getDefault().getPathMatcher( "glob:**.ttf" );

  public static List<Path> walk( final String directory )
      throws URISyntaxException, IOException {
    final List<Path> filenames = new ArrayList<>();
    final var resource = ResourceWalker.class.getResource( directory );

    if( resource != null ) {
      final var uri = resource.toURI();
      final var path = uri.getScheme().equals( "jar" )
          ? newFileSystem( uri, emptyMap() ).getPath( directory )
          : Paths.get( uri );
      final var walk = Files.walk( path, 10 );

      for( final var it = walk.iterator(); it.hasNext(); ) {
        final Path p = it.next();
        if( FILE_MATCHER.matches( p ) ) {
          filenames.add( p );
        }
      }
    }

    return filenames;
  }
}

Điều này linh hoạt hơn một chút để đối sánh với các tên tệp cụ thể vì nó sử dụng ký tự đại diện globbing.


Một phong cách chức năng hơn:

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.function.Consumer;

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

/**
 * Responsible for finding file resources.
 */
public class ResourceWalker {
  private static final PathMatcher FILE_MATCHER =
      FileSystems.getDefault().getPathMatcher( "glob:**.ttf" );

  public static void walk( final String dirName, final Consumer<Path> f )
      throws URISyntaxException, IOException {
    final var resource = ResourceWalker.class.getResource( dirName );

    if( resource != null ) {
      final var uri = resource.toURI();
      final var path = uri.getScheme().equals( "jar" )
          ? newFileSystem( uri, emptyMap() ).getPath( dirName )
          : Paths.get( uri );
      final var walk = Files.walk( path, 10 );

      for( final var it = walk.iterator(); it.hasNext(); ) {
        final Path p = it.next();
        if( FILE_MATCHER.matches( p ) ) {
          f.accept( p );
        }
      }
    }
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.