Làm cách nào tôi có thể nhận được một Tài nguyên thư mục mã nguồn từ trong tệp jar của mình?


139

Tôi có một thư mục / gói tài nguyên trong thư mục gốc của dự án, tôi "không" muốn tải một Tệp nhất định. Nếu tôi muốn tải một Tệp nhất định, tôi sẽ sử dụng class.getResourceAsStream và tôi sẽ ổn !! Những gì tôi thực sự muốn làm là tải một "Thư mục" trong thư mục tài nguyên, lặp trên các Tệp trong Thư mục đó và nhận Luồng đến từng tệp và đọc trong nội dung ... Giả sử rằng tên Tệp không được xác định trước khi chạy ... Tôi nên làm gì? Có cách nào để lấy danh sách các tệp trong Thư mục trong tệp jar của bạn không? Lưu ý rằng tệp Jar có tài nguyên là cùng một tệp jar mà mã đang được chạy ...

Cảm ơn trước...


1
Điều này sẽ giúp: stackoverflow.com/questions/1529611/
Kẻ


5
Điều đầu tiên là về việc giải nén một tệp jar, đây là điều cuối cùng tôi muốn thử, giống như một tình huống nếu tồi tệ hơn đến tồi tệ hơn !! Nếu tôi muốn các tập tin được giải nén, tôi sẽ chỉ cần đặt chúng vào thư mục cài đặt của mình tại thời điểm cài đặt! Tôi muốn truy cập chúng từ bên trong jar và lý do của tôi là, nếu getResourceAsStream có thể truy cập Tệp, Java cũng có thể khám phá các thư mục trong tệp đó, mà không cần phải giải nén nó !! Nhưng cảm ơn ...
Mostafa Zeinali

Câu trả lời:


111

Cuối cùng, tôi tìm thấy giải pháp:

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

Khối thứ hai chỉ hoạt động khi bạn chạy ứng dụng trên IDE (không phải với tệp jar), Bạn có thể xóa nó nếu bạn không thích điều đó.


Cập nhật câu trả lời đúng. Mặc dù đây không phải là điều tôi muốn làm, đặc biệt là trong một mã có hơn 34000 tệp nguồn ... Nhưng nó dường như là giải pháp duy nhất. Cảm ơn bạn.
Mostafa Zeinali

4
FYI, điều này không hoạt động nếu được khởi chạy từ webstart vì getPath trả về một cái gì đó liên quan đến miền của máy chủ.
amos

1
Đừng nghĩ rằng nó sẽ hoạt động nếu đường dẫn nằm trong một cái bình, sẽ đưa ra lỗi này khi chuyển đổi toURI: java.lang.IllegalArgumentException: URI không được phân cấp
Tribbloid

4
Chỉ hoạt động đối với tệp jar đơn, không hoạt động để trích xuất tài nguyên từ các tệp phụ thuộc trong tệp jar khác
Tribbloid

2
Đây không phải là một giải pháp an toàn, bởi vì: 1. Nó giả sử tệp .jar là một tệp cục bộ trên cùng một hệ thống; 2. URL.getPath () không trả về tên tệp hợp lệ, nó chỉ trả về phần đường dẫn của URL, với tất cả các phần trăm thoát còn nguyên vẹn; 3. ProtectionDomain.getCodeSource () có thể trả về null .
VGR

14

Hãy thử như sau.
Tạo đường dẫn tài nguyên "<PathRelativeToThisClassFile>/<ResourceDirectory>" Ví dụ: nếu đường dẫn lớp của bạn là com.abc.package.MyClass và các tệp resoure của bạn nằm trong src / com / abc / pack / resource /:

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

Bạn cũng có thể dùng

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

27
Cảm ơn thời gian và công sức, nhưng điều này KHÔNG hoạt động khi cơ sở mã của bạn nằm trong tệp .jar và tài nguyên của bạn cũng nằm trong Tệp .jar đó. Khi bạn ở trong tệp .jar, bạn không thể tạo Tệp () và bạn cũng không thể nhận URL cho thư mục đó ... Cá nhân tôi đã yên tâm với nó và chỉ liệt kê từng tệp trong XML ... Tôi không ' Tôi nghĩ bạn hoàn toàn có thể làm điều đó cho một thư mục trong một tệp jar ...
Mostafa Zeinali

2
Xin lỗi rằng bạn có vấn đề. Tuy nhiên, nó thực sự hoạt động khi cơ sở mã của bạn nằm trong tệp jar và tài nguyên của bạn cũng nằm trong tệp jar đó. Bạn không tạo Tệp () trên đĩa - bạn đang đọc Tệp trong tệp jar vào bộ nhớ java. Lưu ý rằng ClassLoader khá vui khi xử lý tệp jar tương đương với một thư mục trên đĩa. Dưới đây là chi tiết từ nguồn:
Glen Best

1
Chỉ cần chạy mã trên một jar và nhận được lỗi ... rất tiếc. V xin lỗi, xấu của tôi. Cần xử lý đường dẫn URL jar bằng "Tệp: ...! / Dir1 / dir2 ...". Hai bản sửa lỗi: stackoverflow.com/questions/1429172/ Khắc OR Chuỗi jarPath = dirURL.getPath (). Chuỗi con (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = JarFile mới (URLDecoder.decode (jarPath, "UTF-8")); Bảng liệt kê <JarEntry> mục = jar.entries (); while (entry.hasMoreElements ()) {// làm gì đó}
Glen Best

3
Từ javadoc, rõ ràng phương thức này KHÔNG LUÔN được đảm bảo để trả lại một tệp trong mọi trường hợp. NHƯNG, nếu bạn thực sự đọc Q, OP được đảm bảo 100% để làm việc với các tệp và thư mục bằng cách xây dựng. Q đang yêu cầu truy cập vào các thư mục và tệp - điều này cụ thể hơn là sự trừu tượng hóa tổng quát của API. Chuyển đổi thành tập tin là phù hợp và chính xác để đáp ứng nhu cầu. Xin vui lòng đọc Q đúng trước khi đăng bình luận trái ngược mạnh mẽ như vậy. Chúc mừng.
Glen tốt nhất

7
OP không hoạt động với các tập tin và thư mục. Anh ta đang làm việc với các tài nguyên trong một tệp JAR. Tài nguyên trong tệp JAR không phải là tệp hoặc thư mục và không thể được liệt kê cùng với Filelớp. Mã này không hoạt động với các tài nguyên bên trong tệp JAR. Giai đoạn = Stage.
Hầu tước Lorne

7

Tôi biết điều này là nhiều năm trước. Nhưng chỉ cho những người khác đi qua chủ đề này. Những gì bạn có thể làm là sử dụng getResourceAsStream()phương thức với đường dẫn thư mục và Luồng đầu vào sẽ có tất cả tên tệp từ thư mục đó. Sau đó, bạn có thể nối đường dẫn dir với mỗi tên tệp và gọi getResourceAsStream cho mỗi tệp trong một vòng lặp.


7
Điều này hoạt động tốt trừ khi bạn có kế hoạch làm xáo trộn mọi thứ, khi nó bật ra.
Tustin2121

1
Tôi nghĩ rằng đây phải là giải pháp được chấp nhận vì nó sạch và không yêu cầu xử lý các phiên bản jar và IDE trong các trường hợp riêng biệt. Mặc dù tôi không chắc tại sao getResource không tìm thấy thư mục nhưng getResourceAsStream thì không.
Raghuram Onti Srinivasan

6

Tôi đã có cùng một vấn đề trong tay khi tôi đang cố tải một số cấu hình hadoop từ các tài nguyên được đóng gói trong jar ... trên cả IDE và trên jar (phiên bản phát hành).

Tôi thấy java.nio.file.DirectoryStreamlàm việc tốt nhất để lặp lại nội dung thư mục trên cả hệ thống tập tin cục bộ và 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());
    }
}

Điều này làm việc tốt cho tôi. Chỉ tôi mới chuyển đổi 'Đường dẫn jarFile = Paths.get (jar.toString (). Chuỗi con ("tệp:". Length ()));' với 'Đường dẫn jarFile = Paths.get (jar.toURI ());' để làm cho nó hoạt động trong Windows. Cũng được sử dụng thử với câu lệnh tài nguyên cho đối tượng FileSystem.
Jarle Jacobsen

Điều này làm việc cho tôi! Tôi đã chạy ứng dụng dropwizard trong docker.
nIKUNJ

4

Đoạn mã sau trả về "thư mục" mong muốn là Đường dẫn bất kể nó có ở trong bình hay không.

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

Yêu cầu java 7+.


Đẹp! Mặc dù vậy, tôi phải thêm rằng FileSystem có thể đóng được, điều đó có nghĩa là bạn phải đóng nó tại một số điểm để tài nguyên hệ thống miễn phí. Tất nhiên, đường dẫn được trả về sẽ trở nên không hợp lệ ngay sau đó.
Kirill Gamazkov

Lưu ý rằng trong trường hợp tệp jar, tên của tài nguyên (ở đây là gói + folder) dường như bị bỏ qua khi tạo hệ thống tệp. Do đó getPathkhông trở lại folder/path/to/..., mà chỉ path/to/....
Marcono1234 17/03/19

1

Một giải pháp khác, bạn có thể làm điều đó bằng cách sử dụng ResourceLoadernhư thế này:

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

Đơn giản ... sử dụng OSGi. Trong OSGi, bạn có thể lặp lại các mục của Bundle với findEntries và findPaths.


10
Nếu nó liên quan đến OSGI, tôi hầu như không gọi nó là 'đơn giản'. Nhưng nếu bạn đã sử dụng OSGI ... chắc chắn rồi. Nhưng đề nghị chuyển sang OSGI chỉ để giải quyết vấn đề đặc biệt này có vẻ hơi quá.
Kris

OSGi cũng giải quyết được nhiều vấn đề khác và ngày nay rất dễ dàng để bắt đầu. Xem hướng dẫn về OSGi enRoute
Peter Kriens

Tôi sẽ tránh xa OSGi trừ khi bạn đã được yêu cầu sử dụng nó. Trên Bloatware được thiết kế để giải quyết các vấn đề triển khai của IMO thập niên 90.
user2337270

-1

Trong tệp jar của tôi, tôi có một thư mục có tên là Upload, thư mục này có ba tệp văn bản khác bên trong nó và tôi cần phải có cùng một thư mục và các tệp bên ngoài tệp jar, tôi đã sử dụng mã dưới đây:

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
Xin chào, cảm ơn vì câu trả lời, nhưng, bạn thấy đấy, bạn cần biết tên của từng tệp trong thư mục đó khi viết mã; và bạn phải đặt tên cho từng người một cách rõ ràng. Những gì tôi đang tìm kiếm là một phương pháp lặp lại các tệp trong thư mục và nhận tên của chúng khi chạy, vì vậy tôi có thể thêm tài nguyên vào thư mục mà không cần thay đổi mã, biết mã cũng sẽ xử lý chúng. Cảm ơn thời gian của bạn mặc dù. : 3
Mostafa Zeinali 4/11/2015

-1

Như các câu trả lời khác chỉ ra, một khi các tài nguyên nằm trong một tệp jar, mọi thứ trở nên thực sự xấu xí. Trong trường hợp của chúng tôi, giải pháp này:

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

hoạt động rất tốt trong các thử nghiệm (vì khi các thử nghiệm được chạy, mã không được đóng gói trong tệp jar), nhưng không hoạt động khi ứng dụng thực sự chạy bình thường. Vì vậy, những gì tôi đã làm là ... Tôi mã hóa danh sách các tệp trong ứng dụng, nhưng tôi có một bài kiểm tra đọc danh sách thực tế từ đĩa (có thể làm điều đó vì nó hoạt động trong các bài kiểm tra) và thất bại nếu danh sách thực tế không không khớp với danh sách ứng dụng trả về.

Bằng cách đó, tôi có mã đơn giản trong ứng dụng của mình (không có thủ thuật) tôi chắc chắn rằng tôi đã không quên thêm một mục mới trong danh sách nhờ vào bài kiểm tra.


-25

Liên kết này cho bạn biết làm thế nào.

Điều kỳ diệu là phương thức getResourceAsStream ():

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

20
Không có anh chàng!! Tôi không muốn một tập tin duy nhất !! Tôi muốn có toàn bộ Thư mục, có tệp phụ không được xác định trước thời gian chạy !!!
Mostafa Zeinali
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.