จะแสดงรายการไฟล์ภายในไฟล์ JAR ได้อย่างไร?


114

ฉันมีรหัสนี้ซึ่งอ่านไฟล์ทั้งหมดจากไดเร็กทอรี

    File textFolder = new File("text_directory");

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

มันใช้งานได้ดี มันเติมอาร์เรย์ด้วยไฟล์ทั้งหมดที่ลงท้ายด้วย ".txt" จากไดเร็กทอรี "text_directory"

ฉันจะอ่านเนื้อหาของไดเร็กทอรีในลักษณะเดียวกันภายในไฟล์ 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));

แต่เพียงเพราะฉันรู้ชื่อไฟล์ส่วนที่เหลือฉันต้องโหลดแบบไดนามิก


แค่คิด - ทำไมไม่ zip / jar อิมเมจเป็นไฟล์แยกต่างหากและอ่านรายการจากชั้นเรียนของคุณใน jar อื่น
Vineet Reynolds

3
เนื่องจากจะต้องมีขั้นตอน "พิเศษ" สำหรับการแจกจ่าย / การติดตั้ง :( คุณรู้ไหมผู้ใช้
OscarRyz

เนื่องจากคุณได้สร้าง jar แล้วคุณอาจรวมรายการไฟล์ไว้ในนั้นแทนการใช้กลเม็ดใด ๆ
Tom Hawtin - แทคไลน์

ฉันอาจจะเข้าใจผิด แต่ไหสามารถฝังอยู่ในไหใบอื่นได้ โซลูชันบรรจุภัณฑ์แบบ one-jar (TM) ibm.com/developerworks/java/library/j-onejarทำงานบนพื้นฐานนี้ ยกเว้นในกรณีของคุณคุณไม่ต้องการคลาสโหลดความสามารถ
Vineet Reynolds

คำตอบ:


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

โปรดทราบว่าใน Java 7 คุณสามารถสร้างไฟล์FileSystemจากไฟล์ JAR (zip) จากนั้นใช้กลไกการเดินและการกรองไดเร็กทอรีของ NIO เพื่อค้นหา วิธีนี้จะช่วยให้เขียนโค้ดที่จัดการกับ JAR และไดเร็กทอรี "ระเบิด" ได้ง่ายขึ้น


เดี๋ยวก่อนขอบคุณ ... กำลังมองหาวิธีทำมาสองสามชั่วโมงแล้ว !!
Newtopian

9
ใช่รหัสนี้ใช้ได้ถ้าเราต้องการแสดงรายการทั้งหมดภายในไฟล์ jar นี้ แต่ถ้าฉันแค่ต้องการแสดงรายการไดเร็กทอรีย่อยภายใน jar ตัวอย่างเช่นexample.jar / dir1 / dir2 /ฉันจะแสดงรายการไฟล์ทั้งหมดภายในไดเร็กทอรีย่อยนี้โดยตรงได้อย่างไร หรือฉันต้องการคลายซิปไฟล์ jar นี้? ขอขอบคุณอย่างยิ่งที่คุณช่วย!
Ensom Hodder

กล่าวถึง Java 7 วิธีการเป็น บริษัท จดทะเบียนใน@ acheron55 ของคำตอบ
Vadzim

@Vadzim คุณแน่ใจหรือไม่ว่าคำตอบของ acheron55 สำหรับ Java 7 ฉันไม่พบ Files.walk () หรือ java.util.Stream ใน Java 7 แต่ใน Java 8: docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html
Bruce อา

@BruceSun ใน java 7 คุณสามารถใช้Files.walkFileTree (... )แทนได้
Vadzim

80

รหัสที่ใช้ได้กับทั้งไฟล์ 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());
        }
    }
}

5
FileSystems.newFileSystem()ต้องใช้ a Map<String, ?>ดังนั้นคุณต้องระบุCollections.emptyMap()ว่าจำเป็นต้องส่งคืนพิมพ์ที่เหมาะสม สิ่งนี้ใช้ได้: Collections.<String, Object>emptyMap().
Zero3

6
มหัศจรรย์ !!! แต่ URI uri = MyClass.class.getResource ("/ resources") toURI (); ควรมี MyClass.class.getClassLoader (). getResource ("/ resources") toURI (); คือ getClassLoader () มิฉะนั้นมันก็ไม่ได้ผลสำหรับฉัน
EMM

8
อย่าลืมปิดfileSystem!
gmjonker

3
นี่ควรเป็นคำตอบแรกสำหรับ 1.8 ( walkmethod in Filesใช้ได้เฉพาะใน 1.8) ปัญหาเดียวคือไดเร็กทอรีรีซอร์สปรากฏในFiles.walk(myPath, 1)ไฟล์ไม่ใช่เฉพาะไฟล์ ฉันเดาว่าองค์ประกอบแรกสามารถละเว้นได้
toto_tico

4
myPath = fileSystem.getPath("/resources");ไม่ได้ผลสำหรับฉัน มันไม่พบอะไรเลย มันควรจะเป็น "ภาพ" ในกรณีของฉันและไดเรกทอรี "ภาพ" ก็รวมอยู่ในโถของฉันอย่างแน่นอน!
phip1611

21

คำตอบของ erickson ทำงานได้อย่างสมบูรณ์:

นี่คือรหัสการทำงาน

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

9

ฉันต้องการขยายคำตอบของ 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อินเทอร์เฟซบนไฟล์เดียวกันและอาจรบกวนพวกเขา (แม้ในแอปพลิเคชันเธรดเดียว)
นอกจากนี้ยังไม่ตรวจสอบnulls (เช่นเปิดgetClass().getResource().

อินเทอร์เฟซ Java NIO โดยเฉพาะนี้เป็นเรื่องที่น่าสยดสยองเนื่องจากแนะนำทรัพยากร global / singleton non thread-safe และเอกสารประกอบนั้นคลุมเครืออย่างมาก (ไม่ทราบจำนวนมากเนื่องจากการใช้งานเฉพาะของผู้ให้บริการ) ผลลัพธ์อาจแตกต่างกันไปสำหรับFileSystemผู้ให้บริการรายอื่น(ไม่ใช่ JAR) อาจจะมีเหตุผลที่ดีที่มันเป็นแบบนั้น ฉันไม่รู้ฉันไม่ได้ศึกษาการใช้งาน


1
การซิงโครไนซ์ทรัพยากรภายนอกเช่น FS ไม่มีความรู้สึกมากนักภายใน VM เดียว อาจมีแอปพลิเคชันอื่นเข้าถึงได้จากภายนอก VM ของคุณ นอกจากนี้ในแอปพลิเคชันของคุณเองการล็อกของคุณตามชื่อไฟล์ยังสามารถข้ามได้อย่างง่ายดาย ด้วยสิ่งนี้คุณควรพึ่งพากลไกการซิงโครไนซ์ระบบปฏิบัติการเช่นการล็อกไฟล์
Espinosa

@Espinosa กลไกการล็อกชื่อไฟล์สามารถข้ามได้ทั้งหมด คำตอบของฉันก็ไม่ปลอดภัยเพียงพอเช่นกัน แต่ฉันเชื่อว่านี่เป็นสิ่งที่ดีที่สุดที่คุณจะได้รับจาก Java NIO โดยใช้ความพยายามเพียงเล็กน้อย อาศัยระบบปฏิบัติการในการจัดการการล็อกหรือไม่สามารถควบคุมได้ว่าแอปพลิเคชันใดเข้าถึงไฟล์ใดที่เป็นการปฏิบัติที่ไม่ถูกต้อง IMHO เว้นแต่คุณจะสร้างแอปที่อิงตามต้นทุน - พูดโปรแกรมแก้ไขข้อความ การไม่จัดการการล็อกด้วยตัวคุณเองอาจทำให้เกิดข้อยกเว้นถูกโยนหรือทำให้เธรดปิดกั้นแอปพลิเคชัน - ควรหลีกเลี่ยงทั้งสองอย่าง
Eyal Roth

8

ดังนั้นฉันเดาว่าปัญหาหลักของฉันคือจะรู้ชื่อขวดที่ชั้นเรียนหลักของฉันอาศัยอยู่ได้อย่างไร

สมมติว่าโครงการของคุณบรรจุใน Jar (ไม่จำเป็นต้องเป็นจริง!) คุณสามารถใช้ ClassLoader.getResource () หรือ findResource () ที่มีชื่อคลาส (ตามด้วย. class) เพื่อรับ jar ที่มีคลาสที่กำหนด คุณจะต้องแยกวิเคราะห์ชื่อ jar จาก URL ที่ได้รับกลับมา (ไม่ใช่เรื่องยาก) ซึ่งฉันจะปล่อยให้เป็นแบบฝึกหัดสำหรับผู้อ่าน :-)

อย่าลืมทดสอบในกรณีที่คลาสไม่ได้เป็นส่วนหนึ่งของขวดโหล


1
ฮะ - น่าสนใจที่สิ่งนี้จะถูกปรับเปลี่ยนโดยไม่มีความคิดเห็น ... เราใช้เทคนิคข้างต้นตลอดเวลาและมันก็ใช้ได้ดี
Kevin Day

เป็นปัญหาเก่า แต่สำหรับฉันแล้วสิ่งนี้ดูเหมือนเป็นการแฮ็กที่ดี โหวตกลับเป็นศูนย์ :)
Tuukka Mustonen

โหวตแล้วเนื่องจากนี่เป็นวิธีแก้ปัญหาเดียวที่แสดงไว้ที่นี่สำหรับกรณีที่คลาสไม่มีCodeSource.
คืนสถานะ Monica 2331977

7

ฉันได้พอร์ตคำตอบของ acheron55ไปที่ Java 7 และปิดFileSystemวัตถุ รหัสนี้ทำงานใน IDE ในไฟล์ jar และใน 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;
                }
            });
        }
    }
}

5

นี่คือวิธีการที่ฉันเขียนสำหรับ "เรียกใช้ 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);
    }
}

1
ฉันเดาว่าปัญหาใหญ่คือการรู้ชื่อไฟล์ jar ตั้งแต่แรก มันเป็นโถที่ Main-Class: อาศัยอยู่
OscarRyz

5

นี่คือตัวอย่างของการใช้ไลบรารีReflectionsเพื่อสแกน classpath แบบวนซ้ำโดยรูปแบบชื่อ regex ที่เสริมด้วยGuava perks สองสามรายการเพื่อดึงเนื้อหาทรัพยากร:

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

สิ่งนี้ใช้ได้กับทั้งไหและคลาสระเบิด


ระวังการสะท้อนความเห็นที่ยังคงไม่สนับสนุน Java 9 ขึ้นไป: github.com/ronmamo/reflections/issues/186 มีลิงก์ไปยังไลบรารีของคู่แข่งที่นั่น
Vadzim

3

ไฟล์ jar เป็นเพียงไฟล์ zip ที่มีรายการที่มีโครงสร้าง คุณสามารถเปิดไฟล์ jar ด้วยเครื่องมือ java zip ตามปกติและสแกนเนื้อหาไฟล์ด้วยวิธีนั้นขยายสตรีม ฯลฯ จากนั้นใช้สิ่งนั้นในการเรียก getResourceAsStream และควรเป็น dory ทั้งหมด

แก้ไข / หลังการชี้แจง

ฉันใช้เวลาสักครู่ในการจำบิตและชิ้นส่วนทั้งหมดและฉันแน่ใจว่ามีวิธีที่สะอาดกว่านี้ แต่ฉันอยากเห็นว่าฉันไม่ได้บ้า ในโปรเจ็กต์ 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?
OscarRyz

หากต้องการอ้างถึงไฟล์ JAR เพียงแค่ใช้ "blah.JAR" เป็นสตริง คุณสามารถใช้new File("blah.JAR")เพื่อสร้างอ็อบเจ็กต์ไฟล์ที่แสดงถึง JAR เช่น เพียงแทนที่ "blah.JAR" ด้วยชื่อ JAR ของคุณ
Thomas Owens

ถ้ามันเป็นขวดเดียวกับที่คุณใช้หมดแล้วตัวโหลดคลาสควรจะมองเห็นของในโถ ... ฉันเข้าใจผิดว่าคุณพยายามทำอะไรในตอนแรก
Mikeb

2
ใช่ฉันมีอยู่แล้วปัญหาคือเมื่อฉันต้องการบางสิ่งเช่น: "... getResourceAsStream (" *. jpg "); ... " นั่นคือรายการไฟล์ที่มีอยู่แบบไดนามิก
OscarRyz

3

ด้วยไฟล์ JAR จริงคุณสามารถแสดงรายการเนื้อหาโดยใช้JarFile.entries(). คุณจะต้องรู้ตำแหน่งของไฟล์ JAR - คุณไม่สามารถขอให้ classloader แสดงรายการทุกสิ่งที่จะได้รับ

คุณควรจะหาตำแหน่งของไฟล์ JAR ได้ตาม URL ที่ส่งกลับมาThisClassName.class.getResource("ThisClassName.class")แต่มันอาจจะค่อนข้างยุ่งเล็กน้อย


อ่านคำตอบของคุณคำถามอื่นที่เกิดขึ้น สิ่งที่จะทำให้เกิดการเรียก: this.getClass (). getResource ("/ my_directory"); ควรส่งคืน URL ที่สามารถ .... ใช้เป็นไดเร็กทอรี? Nahh ... ให้ฉันลองดู
OscarRyz

คุณมักจะทราบตำแหน่งของ JAR - อยู่ใน "" ตราบใดที่ชื่อของ JAR เป็นสิ่งที่คุณสามารถใช้ค่าคงที่ของ String ได้ ตอนนี้ถ้าคนไปเปลี่ยนชื่อ JAR ...
Thomas Owens

@ โทมัส: สมมติว่าคุณกำลังเรียกใช้แอปจากไดเรกทอรีปัจจุบัน "java -jar foo / bar / baz.jar" มีอะไรผิดปกติ
Jon Skeet

ฉันเชื่อ (และจะต้องตรวจสอบ) ว่าถ้าคุณมีรหัสใน Jar ของคุณนั่นคือnew File("baz.jar)วัตถุ File จะเป็นตัวแทนของไฟล์ JAR ของคุณ
Thomas Owens

@ โทมัส: ฉันไม่เชื่ออย่างนั้น ฉันเชื่อว่ามันจะสัมพันธ์กับไดเร็กทอรีการทำงานปัจจุบันของกระบวนการ ฉันต้องตรวจสอบด้วย :)
Jon Skeet

3

เมื่อไม่นานมานี้ฉันได้สร้างฟังก์ชันที่ได้รับความดีงามจากภายใน 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
แม้ว่าข้อมูลโค้ดนี้จะช่วยแก้ปัญหาได้ แต่ก็ไม่ได้อธิบายว่าทำไมหรือตอบคำถามได้อย่างไร โปรดใส่คำอธิบายสำหรับโค้ดของคุณเนื่องจากจะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้มาก โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและบุคคลเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำโค้ดของคุณ
Samuel Philipp

ข้อมูลว่างเปล่าเมื่อเรารันโค้ดจากไฟล์ jar
Aguid


1

กลไกที่มีประสิทธิภาพที่สุดสำหรับการแสดงรายการทรัพยากรทั้งหมดใน classpath คือการใช้รูปแบบนี้กับ ClassGraphเนื่องจากจัดการอาร์เรย์ของกลไกข้อกำหนดคลาสพา ธ ที่กว้างที่สุดเท่าที่จะเป็นไปได้รวมถึงระบบโมดูล JPMS ใหม่ (ฉันเป็นผู้เขียน ClassGraph)

จะทราบชื่อไฟล์ 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();
}

มีวิธีอื่น ๆ อีกมากมายในการจัดการกับทรัพยากรเช่นกัน


1
แพ็คเกจที่ดีมากใช้งานได้ง่ายในโครงการ Scala ของฉันขอบคุณ
zslim

0

เป็นวิธีที่แตกต่างในการแสดงรายการ / อ่านไฟล์จาก 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 
    }
});

0

อีกหนึ่งถนน:

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

สิ่งนี้ยืดหยุ่นกว่าเล็กน้อยสำหรับการจับคู่ชื่อไฟล์ที่เฉพาะเจาะจงเนื่องจากใช้ไวด์การ์ด globbing


รูปแบบการใช้งานที่มากขึ้น:

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 );
        }
      }
    }
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.