디렉토리에서 모든 파일을 읽는 코드가 있습니다.
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));
하지만 파일 이름을 알고 있기 때문에 나머지 파일을 동적으로로드해야합니다.
답변
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 및 “폭발 된”디렉토리를 처리하는 코드를 더 쉽게 작성할 수 있습니다.
답변
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());
}
}
}
답변
erickson의 대답 은 완벽하게 작동했습니다.
다음은 작동 코드입니다.
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]));
답변
여러 가지 이유로 매우 안전하지 않은 솔루션이기 때문에 acheron55의 답변 을 확장하고 싶습니다 .
FileSystem
개체를 닫지 않습니다 .FileSystem
개체가 이미 존재 하는지 확인하지 않습니다 .- 스레드로부터 안전하지 않습니다.
이것은 다소 안전한 해결책입니다.
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
동일한 파일에 대해 인터페이스 를 사용하는 코드에 다른 부분이있을 수 있고 (단일 스레드 응용 프로그램에서도) 간섭을 일으킬 수 있기 때문에 이것이 여전히 문제가되는 해결책이라고 말하고 싶습니다 .
또한 null
s를 확인하지 않습니다 (예 : on getClass().getResource()
.
이 특정 Java NIO 인터페이스는 스레드로부터 안전하지 않은 전역 / 싱글 톤 리소스를 도입하고 문서가 매우 모호합니다 (제공 업체별 구현으로 인해 많은 알 수 없음). 결과는 다른 FileSystem
공급자 (JAR 아님)에 따라 다를 수 있습니다 . 아마 그럴만 한 이유가있을 것입니다. 모르겠습니다. 구현을 조사하지 않았습니다.
답변
그래서 내 주된 문제는 내 메인 클래스가 사는 항아리의 이름을 아는 방법입니다.
프로젝트가 Jar에 압축되어 있다고 가정하면 (반드시 사실은 아닙니다!), ClassLoader.getResource () 또는 findResource ()를 클래스 이름 (.class가 뒤 따름)과 함께 사용하여 지정된 클래스가 포함 된 jar를 가져올 수 있습니다. 반환되는 URL에서 jar 이름을 구문 분석해야합니다 (그다지 어렵지 않음). 독자를위한 연습으로 남겨 둘 것입니다. 🙂
클래스가 항아리의 일부가 아닌 경우를 테스트해야합니다.
답변
Java 7에 대한 acheron55의 답변 을 포팅 하고 FileSystem
객체를 닫았습니다 . 이 코드는 IDE, jar 파일 및 Tomcat 7과의 전쟁 내의 jar에서 작동합니다. 그러나 JBoss 7과의 전쟁에서 항아리에서 작동 하지 않습니다 ( 이 게시물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;
}
});
}
}
}
답변
다음은 “패키지에서 모든 JUnits 실행”을 위해 작성한 방법입니다. 필요에 맞게 조정할 수 있어야합니다.
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);
}
}