List<String> getResourceNames (String directoryName)
。例如,给定一个包含文件x/y/z
,a.html
,b.html
和子目录c.html
,d
应该返回包含以下字符串的getResourceNames("x/y/z")
:List<String>
。它应该同时适用于文件系统和jar中的资源。
我知道我可以编写一个
['a.html', 'b.html', 'c.html', 'd']
s,File
s和JarFile
s的快速摘要,但是我不想重新发明轮子。我的问题是,鉴于现有的公共可用库,实现URL
的最快方法是什么? Spring和Apache Commons堆栈都是可行的。#1 楼
自定义扫描仪实施您自己的扫描仪。例如:
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
使用Spring Framework中的
PathMatchingResourcePatternResolver
。Ronmamo Reflections
对于大的CLASSPATH值,其他技术在运行时可能很慢。更快的解决方案是使用ronmamo的Reflections API,该API在编译时对搜索进行预编译。
评论
如果使用Reflections,则实际需要的是:new Reflections(“ my.package”,new ResourcesScanner())。getResources(pattern)
– zapp
13年3月16日在13:16
从jar文件执行时,第一个解决方案有效吗?对我来说,事实并非如此。我可以通过这种方式读取文件,但是无法读取目录。
–华伦天奴·楚马克(Valentina Chumak)
16-10-6在12:28
此答案中描述的第一种方法-将路径读取为资源,但至少在OpenJDK 1.8中,当资源与可执行代码位于同一JAR中时,它似乎不起作用。失败很奇怪-从JVM的文件处理逻辑的深处抛出了NullPointerException。好像设计人员并没有真正预料到这种资源使用情况,只有部分实现。即使它可以在某些JDK或环境上运行,也似乎没有记录这种行为。
–凯文·布恩
17年9月18日在7:21
您不能像这样读取目录,因为除了文件流之外没有目录。这个答案是错误的一半。
– Thomas Decaux
17年11月15日在8:37
第一种方法似乎不起作用-至少在破坏资源之前从开发环境运行时。调用带有目录相对路径的getResourceAsStream()会导致FileInputStream(File),该文件定义为如果文件是目录则抛出FileNotFoundException。
–安迪·托马斯(Andy Thomas)
17年12月6日在23:14
#2 楼
这是代码源:forums.devx.com/showthread.php?t=153784import 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
评论
Questioner知道如何使用标准的Java类来实现它,但是他想知道如何利用现有的(Spring,Apache Commons)库。
–耶隆·罗森伯格(Jeroen Rosenberg)
10-10-13在11:54
@Jeroen Rosenberg最终选择的还有另一种方法:)
–Jigar Joshi
2010-10-13 12:26
我发现此解决方案对您不使用Spring的情况很有用-仅由于此功能而对Spring添加依赖项是荒谬的。所以谢谢! “脏”但有用:) P.S.一个人可能想要在getResources方法中使用File.pathSeparator而不是硬编码的:
–Timur
2011-09-27 14:05
该代码示例与系统有关,请使用以下示例:final String [] classPathElements = classPath.split(System.pathSeparator);
–编译
2012年2月19日在20:53
注意:java.class.path系统属性可能不包含您正在其下运行的实际类路径。您也可以查看类加载器,并查询它从中加载类的URL。当然,另一个警告是,如果您在SecurityManager下执行此操作,则ZipFile构造函数可以拒绝您对该文件的访问,因此您应该抓住这一点。
–特雷卡兹
16年2月8日在22:59
#3 楼
使用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"));
评论
有什么方法可以实现相同而无需导入google依赖项?我想避免仅具有依赖关系来执行此任务。
– Enrico Giurin
19 Mar 5 '19 at 22:25
Reflections不是Google图书馆。
–卢克·哈奇森(Luke Hutchison)
19-10-5在10:31
也许是过去。我的回答是从2015年开始的,在2015年之前,我确实使用“ Google Reflections”一词找到了对该库的引用。我看到代码已经稍微移动了一点,并且从这里的code.google.com移到了github这里
– Nico du Toit
19-10-6在18:40
@NSduToit可能是将代码托管在Google Code上的一种人工产物(这不是常见错误),而不是Google的著作权声明。
–马特b
1月7日19:21
#4 楼
如果您使用apache commonsIO,则可用于文件系统(可选,带有扩展过滤器):Collection<File> files = FileUtils.listFiles(new File("directory/"), null, false);
以及资源/类路径:
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)
在通话之前,并结合使用两者...
评论
文件!=资源
– djechlin
2013年12月13日在21:27
当然,资源可能并不总是文件,但问题是关于源自文件/目录的资源。因此,如果您要检查其他位置,请使用示例代码。如果您的资源中有用于默认配置的config.xml,并且应该可以加载外部config.xml(如果存在)...
–Rob
2013年12月19日16:03
为什么资源不是文件?资源是归档到zip归档中的文件。它们被加载到内存中并以特定方式访问,但是我看不到为什么它们不是文件。任何非“归档文件中的文件”资源?
–迈克
17年5月18日在7:20
当目标资源是驻留在JAR文件中的目录时(即JAR包含Main应用和目标目录),则无法正常工作(NullPointerException)
–卡利托斯之路
17年6月26日在23:51
#5 楼
因此,就PathMatchingResourcePatternResolver而言,这是代码中需要的内容:@Autowired
ResourcePatternResolver resourceResolver;
public void getResources() {
resourceResolver.getResources("classpath:config/*.xml");
}
评论
如果您想在所有类路径中搜索所有可能的资源,则只需添加一小部分就可以使用classpath *:config / *。xml
–revau.lt
19年1月8日在13:31
#6 楼
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>
#7 楼
我的方法,没有弹簧,在单元测试期间使用:URI uri = TestClass.class.getResource("/resources").toURI();
Path myPath = Paths.get(uri);
Stream<Path> walk = Files.walk(myPath, 1);
for (Iterator<Path> it = walk.iterator(); it.hasNext(); ) {
Path filename = it.next();
System.out.println(filename);
}
评论
运行jar时不起作用:Paths.get(uri)上的java.nio.file.FileSystemNotFoundException
–error1009
4月29日8:29
#8 楼
结合了Rob的回应。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
}
评论
如何获取所有资源:即以/'或.`或./开头,但实际上都无效
– StephenBoesch
1月5日12:23
#9 楼
这应该可以工作(如果无法使用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;
}
评论
这是最近收到的一份反对票-如果您发现它有问题,请分享,谢谢!
– fl0w
18年8月14日在6:34
谢谢@ArnoUnkrig-如果您愿意,请分享更新的解决方案
– fl0w
18年11月9日在12:42
#10 楼
使用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中的资源。
评论
我想在/ BOOT-INF / classes / static / stories下获得文件夹名称。我用@Value(“ classpath:static / stories / *”)尝试您的代码,并且在运行JAR时它返回一个空列表。它适用于类路径中的资源,但不适用于JAR中的资源。
– P-S
19年2月27日在13:00
@Value(“ classpath:x / y / z / *”)私有Resource []资源;向我做了把戏。有搜索时间!谢谢!
–鲁道夫·施密特(Rudolf Schmidt)
19 Mar 29 '19在10:09
#11 楼
列出类路径中所有资源的最健壮的机制当前是将这种模式与ClassGraph一起使用,因为它可以处理尽可能广泛的类路径规范机制,包括新的JPMS模块系统。 (我是ClassGraph的作者。)List<String> resourceNames;
try (ScanResult scanResult = new ClassGraph().whitelistPaths("x/y/z").scan()) {
resourceNames = scanResult.getAllResources().getNames();
}
#12 楼
我认为您可以利用[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]类的方法来执行诸如复制,移动和重命名文件以及修改文件属性之类的操作。The [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
#13 楼
即使将我的资源放在资源文件夹中并遵循上述答案,这两个答案对我也不起作用。造成欺骗的原因是:@Value("file:*/**/resources/**/schema/*.json")
private Resource[] resources;
#14 楼
基于上述@rob的信息,我创建了要发布到公共领域的实现:private static List<String> getClasspathEntriesByPath(String path) throws IOException {
InputStream is = Main.class.getClassLoader().getResourceAsStream(path);
StringBuilder sb = new StringBuilder();
while (is.available()>0) {
byte[] buffer = new byte[1024];
sb.append(new String(buffer, Charset.defaultCharset()));
}
return Arrays
.asList(sb.toString().split("\n")) // Convert StringBuilder to individual lines
.stream() // Stream the list
.filter(line -> line.trim().length()>0) // Filter out empty lines
.collect(Collectors.toList()); // Collect remaining lines into a List again
}
虽然我不希望
getResourcesAsStream
能够那样工作在目录上,它确实可以运行,并且效果很好。
评论
相关答案:stackoverflow.com/a/30149061/4102160检查此项目,解决资源文件夹扫描:github.com/fraballi/resources-folder-scanner