134

ディレクトリからすべてのファイルを読み取るこのコードがあります。

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

(これは、「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));

しかし、ファイル名を知っているという理由だけで、残りの部分については動的にロードする必要があります。

4

17 に答える 17

107
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と「展開された」ディレクトリを処理するコードを簡単に記述できるようになります。

于 2009-09-15T19:48:50.230 に答える
91

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());
        }
    }
}
于 2015-01-21T00:39:57.233 に答える
22

エリクソンの答え は完璧に機能しました:

これが動作するコードです。

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]));
于 2009-09-16T21:41:45.613 に答える
13

いくつかの理由から、これは非常に安全ではないソリューションであるため、 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同じファイル上でインターフェイスを使用するコード内の他の部分があり、それらに干渉する可能性があるため、これは依然として問題のある解決策であると言えます(シングルスレッドアプリケーションでも)。
また、sをチェックしませんnull(たとえば、getClass().getResource()

この特定のJavaNIOインターフェースは、グローバル/シングルトンの非スレッドセーフリソースを導入し、そのドキュメントが非常にあいまいであるため、一種の恐ろしいものです(プロバイダー固有の実装のために多くの不明な点があります)。結果は他のFileSystemプロバイダー(JARではない)によって異なる場合があります。たぶんそれがそのようであるのには正当な理由があります。私は知りません、私は実装を研究していません。

于 2016-06-09T15:54:10.217 に答える
9

ですから、私の主な問題は、私のメインクラスが住んでいる瓶の名前をどうやって知るかということだと思います。

プロジェクトがJarにパックされていると仮定すると(必ずしもtrueではありません!)、ClassLoader.getResource()またはfindResource()をクラス名(後に.classが続く)とともに使用して、特定のクラスを含むjarを取得できます。返されるURLからjar名を解析する必要があります(それほど難しいことではありません)。これは読者のための演習として残しておきます:-)

クラスがjarの一部ではない場合は必ずテストしてください。

于 2009-09-16T06:41:30.507 に答える
8

acheron55の回答をJava7に移植し、FileSystemオブジェクトを閉じました。このコードは、IDE、jarファイル、およびTomcat7での戦争中の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;
                }
            });
        }
    }
}
于 2016-10-11T09:38:07.400 に答える
6

これは、 Reflectionsライブラリを使用して、リソースの内容をフェッチするためのいくつかのGuava特典で拡張された正規表現名パターンによってクラスパスを再帰的にスキャンする例です。

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

これは、jarクラスと展開されたクラスの両方で機能します。

于 2015-08-03T10:03:16.653 に答える
5

これが私が「パッケージの下ですべてのJUnitを実行する」ために書いたメソッドです。あなたはそれをあなたのニーズに適応させることができるはずです。

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);
    }
}
于 2009-09-15T19:39:46.613 に答える
3

jarファイルは、構造化されたマニフェストを含む単なるzipファイルです。通常のJavazipツールを使用してjarファイルを開き、ファイルの内容をそのようにスキャンしたり、ストリームを膨らませたりすることができます。次に、それをgetResourceAsStream呼び出しで使用すると、すべてが厄介なものになります。

編集/明確化後

すべての断片を覚えるのに1分かかりました。それを行うためのよりクリーンな方法があると確信していますが、私は頭がおかしくないことを確認したかったのです。私のプロジェクトでは、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
于 2009-09-15T19:35:19.557 に答える
3

実際のJARファイルを指定すると、を使用してコンテンツを一覧表示できますJarFile.entries()。ただし、JARファイルの場所を知る必要があります。クラスローダーに取得できるすべてのものをリストするように要求することはできません。

から返されたURLに基​​づいてJARファイルの場所を特定できるはずですがThisClassName.class.getResource("ThisClassName.class")、少し面倒かもしれません。

于 2009-09-15T19:37:08.600 に答える
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;
}
于 2011-05-27T23:28:40.710 に答える
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;
}
于 2019-08-24T19:23:51.750 に答える
1

JarScanと呼ばれる2つの非常に便利なユーティリティがあります。

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

この質問も参照してください:JarScan、特定のクラスのすべてのサブフォルダー内のすべてのJARファイルをスキャンします

于 2009-10-05T07:21:31.197 に答える
1

クラスパス内のすべてのリソースを一覧表示するための最も堅牢なメカニズムは、現在、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();
}

リソースを処理する方法は他にもたくさんあります。

于 2019-10-05T11:20:46.247 に答える
1

ワイルドカードグロブを使用しているため、特定のファイル名を照合するための柔軟性が少し高い道路用にもう1つ。機能的なスタイルでは、これは次のようになります。

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ステートメントまたは同様の制御構造内で使用して、ストリームの操作が完了した後、ストリームの開いているディレクトリがすぐに閉じられるようにする必要があります。

同様に、newFileSystem閉じる必要がありますが、ウォーカーがファイルシステムパスにアクセスする前に閉じる必要はありません。

于 2020-06-23T06:26:04.597 に答える
1

すでにSpringを使用している場合は、を利用できますPathMatchingResourcePatternResolver

たとえばimages、リソース内のフォルダからすべてのPNGファイルを取得するには

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()));
}
于 2021-06-29T13:37:03.677 に答える
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 
    }
});
于 2015-06-17T03:00:21.373 に答える