メソッドList<String> getResourceNames (String directoryName)
のような、特定のクラスパスディレクトリからすべてのリソース名のリストを取得する方法を探しています。
たとえば、ファイルx/y/z
、a.html
、b.html
およびサブディレクトリd
、getResourceNames("x/y/z")
を含むクラスパスディレクトリc.html
が与えられた場合、List<String>
次の文字列を含む:['a.html', 'b.html', 'c.html', 'd']
。
ファイルシステムとjarの両方のリソースで機能するはずです。
File
s、JarFile
s、およびURL
sを使用して簡単なスニペットを作成できることは知っていますが、車輪を再発明したくありません。私の質問は、既存の公的に利用可能なライブラリを考えると、getResourceNames
を実装する最も速い方法は何ですか? SpringとApache Commonsの両方のスタックが実行可能です。
あなた自身のスキャナを実装してください。例えば:
private List<String> getResourceFiles(String path) throws IOException {
List<String> filenames = new ArrayList<>();
try (
InputStream in = getResourceAsStream(path);
BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
String resource;
while ((resource = br.readLine()) != null) {
filenames.add(resource);
}
}
return filenames;
}
private InputStream getResourceAsStream(String resource) {
final InputStream in
= getContextClassLoader().getResourceAsStream(resource);
return in == null ? getClass().getResourceAsStream(resource) : in;
}
private ClassLoader getContextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
Spring Frameworkの PathMatchingResourcePatternResolver
を使用してください。
他の手法では、巨大なCLASSPATH値に対して実行時に遅くなる可能性があります。より速い解決策は、コンパイル時に検索をプリコンパイルするronmamoの Reflections API を使うことです。
これがコードです
出典:forums.devx.com/showthread.php?t=153784
import Java.io.File;
import Java.io.IOException;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.Enumeration;
import Java.util.regex.Pattern;
import Java.util.Zip.ZipEntry;
import Java.util.Zip.ZipException;
import Java.util.Zip.ZipFile;
/**
* list resources available from the classpath @ *
*/
public class ResourceList{
/**
* for all elements of Java.class.path get a Collection of resources Pattern
* pattern = Pattern.compile(".*"); gets all resources
*
* @param pattern
* the pattern to match
* @return the resources in the order they are found
*/
public static Collection<String> getResources(
final Pattern pattern){
final ArrayList<String> retval = new ArrayList<String>();
final String classPath = System.getProperty("Java.class.path", ".");
final String[] classPathElements = classPath.split(System.getProperty("path.separator"));
for(final String element : classPathElements){
retval.addAll(getResources(element, pattern));
}
return retval;
}
private static Collection<String> getResources(
final String element,
final Pattern pattern){
final ArrayList<String> retval = new ArrayList<String>();
final File file = new File(element);
if(file.isDirectory()){
retval.addAll(getResourcesFromDirectory(file, pattern));
} else{
retval.addAll(getResourcesFromJarFile(file, pattern));
}
return retval;
}
private static Collection<String> getResourcesFromJarFile(
final File file,
final Pattern pattern){
final ArrayList<String> retval = new ArrayList<String>();
ZipFile zf;
try{
zf = new ZipFile(file);
} catch(final ZipException e){
throw new Error(e);
} catch(final IOException e){
throw new Error(e);
}
final Enumeration e = zf.entries();
while(e.hasMoreElements()){
final ZipEntry ze = (ZipEntry) e.nextElement();
final String fileName = ze.getName();
final boolean accept = pattern.matcher(fileName).matches();
if(accept){
retval.add(fileName);
}
}
try{
zf.close();
} catch(final IOException e1){
throw new Error(e1);
}
return retval;
}
private static Collection<String> getResourcesFromDirectory(
final File directory,
final Pattern pattern){
final ArrayList<String> retval = new ArrayList<String>();
final File[] fileList = directory.listFiles();
for(final File file : fileList){
if(file.isDirectory()){
retval.addAll(getResourcesFromDirectory(file, pattern));
} else{
try{
final String fileName = file.getCanonicalPath();
final boolean accept = pattern.matcher(fileName).matches();
if(accept){
retval.add(fileName);
}
} catch(final IOException e){
throw new Error(e);
}
}
}
return retval;
}
/**
* list the resources that match args[0]
*
* @param args
* args[0] is the pattern to match, or list all resources if
* there are no args
*/
public static void main(final String[] args){
Pattern pattern;
if(args.length < 1){
pattern = Pattern.compile(".*");
} else{
pattern = Pattern.compile(args[0]);
}
final Collection<String> list = ResourceList.getResources(pattern);
for(final String name : list){
System.out.println(name);
}
}
}
Springを使用している場合 PathMatchingResourcePatternResolver をご覧ください。
Google Reflectionsを使う:
クラスパス上のすべてを取得します。
Reflections reflections = new Reflections(null, new ResourcesScanner());
Set<String> resourceList = reflections.getResources(x -> true);
別の例 - some.packageから拡張子。csvを持つすべてのファイルを取得する:
Reflections reflections = new Reflections("some.package", new ResourcesScanner());
Set<String> fileNames = reflections.getResources(Pattern.compile(".*\\.csv"));
もしあなたがApache commonsIOを使うなら、あなたはファイルシステムのために使うことができます(オプションで拡張子フィルタを使って):
Collection<File> files = FileUtils.listFiles(new File("directory/"), null, false);
resources /クラスパスの場合:
List<String> files = IOUtils.readLines(MyClass.class.getClassLoader().getResourceAsStream("directory/"), Charsets.UTF_8);
"directoy /"がファイルシステムにあるのかリソースにあるのかわからない場合は、
if (new File("directory/").isDirectory())
または
if (MyClass.class.getClassLoader().getResource("directory/") != null)
呼び出しの前に、両方を組み合わせて使用する...
そのため、PathMatchingResourcePatternResolverに関しては、これがコードに必要なものです。
@Autowired
ResourcePatternResolver resourceResolver;
public void getResources() {
resourceResolver.getResources("classpath:config/*.xml");
}
Spring framework
のPathMatchingResourcePatternResolver
は、これらのことに本当に素晴らしいです。
private Resource[] getXMLResources() throws IOException
{
ClassLoader classLoader = MethodHandles.lookup().getClass().getClassLoader();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
return resolver.getResources("classpath:x/y/z/*.xml");
}
Mavenの依存関係:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>LATEST</version>
</dependency>
ロブの反応を組み合わせて使った。
final String resourceDir = "resourceDirectory/";
List<String> files = IOUtils.readLines(Thread.currentThread.getClass().getClassLoader().getResourceAsStream(resourceDir), Charsets.UTF_8);
for(String f : files){
String data= IOUtils.toString(Thread.currentThread.getClass().getClassLoader().getResourceAsStream(resourceDir + f));
....process data
}
これはうまくいくはずです(springが選択肢ではない場合)。
public static List<String> getFilenamesForDirnameFromCP(String directoryName) throws URISyntaxException, UnsupportedEncodingException, IOException {
List<String> filenames = new ArrayList<>();
URL url = Thread.currentThread().getContextClassLoader().getResource(directoryName);
if (url != null) {
if (url.getProtocol().equals("file")) {
File file = Paths.get(url.toURI()).toFile();
if (file != null) {
File[] files = file.listFiles();
if (files != null) {
for (File filename : files) {
filenames.add(filename.toString());
}
}
}
} else if (url.getProtocol().equals("jar")) {
String dirname = directoryName + "/";
String path = url.getPath();
String jarPath = path.substring(5, path.indexOf("!"));
try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.startsWith(dirname) && !dirname.equals(name)) {
URL resource = Thread.currentThread().getContextClassLoader().getResource(name);
filenames.add(resource.toString());
}
}
}
}
}
return filenames;
}
Springを使えば簡単です。それがファイル、フォルダ、あるいは複数のファイルであっても、チャンスがある、あなたは注射を通してそれをすることができます。
この例は、x/y/z
フォルダーにある複数のファイルの挿入を示しています。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
@Service
public class StackoverflowService {
@Value("classpath:x/y/z/*")
private Resource[] resources;
public List<String> getResourceNames() {
return Arrays.stream(resources)
.map(Resource::getFilename)
.collect(Collectors.toList());
}
}
これはファイルシステムとJARの中のリソースに対して機能します。
リソースをリソースフォルダに入れて、上記の回答に従ったにもかかわらず、どちらの回答も機能しませんでした。トリックをしたのは:
@Value("file:*/**/resources/**/schema/*.json")
private Resource[] resources;
これを実現するために[Zip File System Provider] [1]を利用できると思います。 FileSystems.newFileSystem
を使うとき、そのZipの中のオブジェクトを「通常の」ファイルとして扱うことができるように見えます。
上記のリンクされたドキュメントでは:
FileSystems.newFileSystem
メソッドに渡されるJava.util.MapオブジェクトにZipファイルシステムの設定オプションを指定します。 Zipファイルシステムのプロバイダ固有の設定プロパティについては、[Zipファイルシステムのプロパティ] [2]のトピックを参照してください。Zipファイルシステムのインスタンスを取得したら、[
Java.nio.file.FileSystem
] [3]および[Java.nio.file.Path
] [4]クラスのメソッドを呼び出して、ファイルのコピー、移動、名前の変更などの操作を実行したり、ファイルを変更したりできます。属性.
[Java 11の状態] [5]にあるjdk.zipfs
モジュールのドキュメント:
Zipファイルシステムプロバイダは、ZipファイルまたはJARファイルをファイルシステムとして扱い、ファイルの内容を操作する機能を提供します。 Zipファイルシステムプロバイダは、インストールされていれば[
FileSystems.newFileSystem
] [6]で作成できます。
これは私があなたの例のリソースを使ってした人為的な例です。 .Zip
は.jar
ですが、代わりにクラスパスリソースを使用するようにコードを修正することができます。
セットアップ
cd /tmp
mkdir -p x/y/z
touch x/y/z/{a,b,c}.html
echo 'hello world' > x/y/z/d
Zip -r example.Zip x
Java
import Java.io.IOException;
import Java.net.URI;
import Java.nio.file.FileSystem;
import Java.nio.file.FileSystems;
import Java.nio.file.Files;
import Java.util.Collections;
import Java.util.stream.Collectors;
public class MkobitZipRead {
public static void main(String[] args) throws IOException {
final URI uri = URI.create("jar:file:/tmp/example.Zip");
try (
final FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.emptyMap());
) {
Files.walk(zipfs.getPath("/")).forEach(path -> System.out.println("Files in Zip:" + path));
System.out.println("-----");
final String manifest = Files.readAllLines(
zipfs.getPath("x", "y", "z").resolve("d")
).stream().collect(Collectors.joining(System.lineSeparator()));
System.out.println(manifest);
}
}
}
出力
Files in Zip:/
Files in Zip:/x/
Files in Zip:/x/y/
Files in Zip:/x/y/z/
Files in Zip:/x/y/z/c.html
Files in Zip:/x/y/z/b.html
Files in Zip:/x/y/z/a.html
Files in Zip:/x/y/z/d
-----
hello world