簡體   English   中英

如何從我的 jar 文件中獲取資源“文件夾”?

[英]How can I get a resource "Folder" from inside my jar File?

我的項目根目錄中有一個資源文件夾/包,我“不想”加載某個文件。 如果我想加載某個文件,我會使用 class.getResourceAsStream ,我會沒事的!! 我真正想要做的是在資源文件夾中加載一個“文件夾”,在該文件夾內的文件上循環並獲取每個文件的流並讀取內容......假設文件名在運行之前沒有確定... 我該怎么辦? 有沒有辦法在您的 jar 文件中獲取文件夾內的文件列表? 請注意,帶有資源的 Jar 文件與運行代碼的 jar 文件相同...

最后,我找到了解決方案:

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

當您在 IDE 上運行應用程序(不是使用 jar 文件)時,第二個塊才起作用,如果您不喜歡,可以將其刪除。

請嘗試以下操作。
使資源路徑"<PathRelativeToThisClassFile>/<ResourceDirectory>"例如,如果您的類路徑是 com.abc.package.MyClass 並且您的資源文件在 src/com/abc/package/resources/ 中:

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

你也可以使用

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

我知道這是很多年前的事了。 但只是為其他人遇到這個話題。 您可以做的是將getResourceAsStream()方法與目錄路徑一起使用,並且輸入 Stream 將具有該目錄中的所有文件名。 之后,您可以將目錄路徑與每個文件名連接起來,並在循環中為每個文件調用 getResourceAsStream。

當我嘗試從 jar 中打包的資源加載一些 hadoop 配置時,我遇到了同樣的問題……在 IDE 和 jar(發布版本)上。

我發現java.nio.file.DirectoryStream最適合在本地文件系統和 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());
    }
}

以下代碼將所需的“文件夾”作為 Path 返回,無論它是否在 jar 內。

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

需要 Java 7+。

另一種解決方案,您可以像這樣使用ResourceLoader來做到這一點:

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

正如其他答案所指出的那樣,一旦資源位於 jar 文件中,事情就會變得非常糟糕。 在我們的例子中,這個解決方案:

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

在測試中工作得很好(因為當測試運行時,代碼沒有打包在 jar 文件中),但在應用程序實際正常運行時不起作用。 所以我所做的是......我對應用程序中的文件列表進行了硬編碼,但是我有一個測試從磁盤讀取實際列表(可以這樣做,因為它在測試中有效)並且如果實際列表沒有則失敗'與應用返回的列表不匹配。

這樣我的應用程序中就有了簡單的代碼(沒有技巧),而且我確信由於測試,我沒有忘記在列表中添加一個新條目。

下面的代碼從自定義資源目錄中獲取 .yaml 文件。

ClassLoader classLoader = this.getClass().getClassLoader();
            URI uri = classLoader.getResource(directoryPath).toURI();
    
            if("jar".equalsIgnoreCase(uri.getScheme())){
                Pattern pattern = Pattern.compile("^.+" +"/classes/" + directoryPath + "/.+.yaml$");
                log.debug("pattern {} ", pattern.pattern());
                ApplicationHome home = new ApplicationHome(SomeApplication.class);
                JarFile file = new JarFile(home.getSource());
                Enumeration<JarEntry>  jarEntries = file.entries() ;
                while(jarEntries.hasMoreElements()){
                    JarEntry entry = jarEntries.nextElement();
                    Matcher matcher = pattern.matcher(entry.getName());
                    if(matcher.find()){
                        InputStream in =
                                file.getInputStream(entry);
                        //work on the stream
    
    
                    }
                }
    
            }else{
                //When Spring boot application executed through Non-Jar strategy like through IDE or as a War.
    
                String path = uri.getPath();
                File[] files = new File(path).listFiles();
               
                for(File file: files){
                    if(file != null){
                        try {
                            InputStream is = new FileInputStream(file);
                           //work on stream
                            
                        } catch (Exception e) {
                            log.error("Exception while parsing file yaml file {} : {} " , file.getAbsolutePath(), e.getMessage());
                        }
                    }else{
                        log.warn("File Object is null while parsing yaml file");
                    }
                }
            }
             

如果您使用 Spring,您可以使用org.springframework.core.io.support.PathMatchingResourcePatternResolver並處理Resource對象而不是文件。 這在 Jar 文件內部和外部運行時有效。

PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver();
Resource[] resources = r.getResources("/myfolder/*");

然后您可以使用getInputStream和來自getFilename的文件名訪問數據。

請注意,如果您在從 Jar 運行時嘗試使用getFile ,它仍然會失敗。

簡單...使用 OSGi。 在 OSGi 中,您可以使用 findEntries 和 findPaths 遍歷 Bundle 的條目。

在我的 jar 文件中,我有一個名為 Upload 的文件夾,該文件夾中有另外三個文本文件,我需要在 jar 文件之外擁有一個完全相同的文件夾和文件,我使用了以下代碼:

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

這個鏈接告訴你如何。

神奇的是 getResourceAsStream() 方法:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM