JAR 파일 내의 파일을 나열하려면 어떻게 해야 합니까?
나는 디렉토리의 모든 파일을 읽는 코드를 가지고 있다.
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"));
(「Company Logo」는 「하드 코드」이기 때문에 동작합니다만, JAR 파일내의 이미지의 수는 10 ~200 의 가변 길이입니다).
EDIT
그래서 제 주된 문제는 다음과 같습니다.메인 클래스가 있는 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... */
}
7에서는 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());
}
}
}
에릭슨의 대답은 딱 들어맞았다.
여기 작업 코드가 있습니다.
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('s'가 켜짐)getClass().getResource()
.
이 특정 Java NIO 인터페이스는 글로벌/싱글톤 비스레드 세이프 리소스를 도입하고 문서화가 매우 모호하기 때문에 좀 끔찍합니다(프로바이더 고유의 구현으로 인해 알 수 없는 경우가 많습니다).는 그 외의 경우에 수 .FileSystem
jar바외제 maybejar외외 maybe maybe maybe maybe maybe for for for for for for for i그럴 만한 이유가 있을지도 모릅니다.실현에 대해서는 아직 조사하지 않았습니다.
그래서 내 주된 문제는 우리 반 아이들이 살고 있는 항아리의 이름을 어떻게 아는가 하는 것 같아.
프로젝트가 Jar(반드시 true는 아님)에 포함되어 있다고 가정하면 ClassLoader.getResource() 또는 findResource()와 클래스 이름(.class 뒤에 있음)을 사용하여 지정된 클래스를 포함하는 jar를 가져올 수 있습니다.반환되는 URL에서 jar name을 해석해야 합니다(그렇게 어려운 것은 아닙니다).이거는 독자를 위한 연습으로 남겨두겠습니다:-)
클래스가 항아리에 포함되지 않은 경우 반드시 테스트하십시오.
acheron55의 답변을 Java 7에 포팅하고FileSystem
물건.이 코드는 Tomcat 7의 전쟁 내 IDE, jar 파일 및 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;
}
});
}
}
}
다음은 Reflections 라이브러리를 사용하여 리소스 내용을 가져오는 몇 가지 Guava 권한으로 증강된 regex 이름 패턴을 통해 클래스 경로를 재귀적으로 검색하는 예제입니다.
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);
}
이것은 항아리와 폭발 클래스 모두에서 작동합니다.
"모든 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);
}
}
jar 파일은 구조화된 매니페스토를 가진 zip 파일입니다.일반적인 Java zip 도구로 jar 파일을 열고 파일 내용을 스캔하거나 스트림을 부풀리는 등의 방법으로 실행할 수 있습니다.그런 다음 getResourceAsStream 호출에서 사용하면 모든 것이 정상입니다.
편집 / 설명 후
모든 조각들을 기억하는 데 1분이 걸렸고 더 나은 방법이 있을 거라고 확신하지만, 나는 내가 미쳤지 않은지 보고 싶었다.프로젝트 이미지에서.jpg는 메인 jar 파일의 일부에 있는 파일입니다.메인 클래스의 클래스 로더(SomeClass는 엔트리 포인트)를 가져와 image.jpg 리소스를 검출하기 위해 사용합니다.그런 다음 이 이미지에 삽입하기 위한 스트리밍 마법을 사용합니다.Input Stream과 관련된 모든 것이 정상입니다.
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
실제 JAR 파일을 지정하면 다음을 사용하여 내용을 나열할 수 있습니다.JarFile.entries()
JAR 파일의 위치를 알아야 합니다.클래스로더에게 입수할 수 있는 모든 것을 나열하도록 요구할 수는 없습니다.
에서 반환된 URL을 기반으로 JAR 파일의 위치를 알아낼 수 있습니다.ThisClassName.class.getResource("ThisClassName.class")
조금 흔들릴 수도 있습니다.
얼마 전에 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;
}
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;
}
참고로 이미 Spring을 사용하고 있다면PathMatchingResourcePatternResolver
.
예를 들어, 모든 PNG 파일을images
in resources 내 폴더
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()));
}
JarScan이라고 불리는 두 가지 매우 유용한 유틸리티가 있습니다.
다음 질문도 참조하십시오.JarScan, 모든 하위 폴더에 있는 모든 JAR 파일에서 특정 클래스를 검색합니다.
ClassPath 내의 모든 리소스를 나열하는 가장 강력한 메커니즘은 현재 ClassGraph에서 이 패턴을 사용하는 것입니다.이는 새로운 JPMS 모듈 시스템을 포함하여 가능한 한 광범위한 클래스 경로 지정 메커니즘을 처리하기 때문입니다.(Class Graph의 저자입니다.)
메인 클래스가 있는 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();
}
와일드카드글로빙을 사용하기 때문에 특정 파일명을 매칭하기 위해 좀 더 유연한 도로용입니다.기능적 스타일은 다음과 같습니다.
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 문 또는 유사한 제어 구조 내에서 사용해야 합니다.
저저마마 likewise likewise likewise likewise likewise likewise likewise likewise.newFileSystem
워커가 파일 시스템 경로를 방문하기 전에는 닫아야 합니다.
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
}
});
언급URL : https://stackoverflow.com/questions/1429172/how-to-list-the-files-inside-a-jar-file
'sourcecode' 카테고리의 다른 글
bcmath 모듈 설치 방법 (0) | 2022.09.23 |
---|---|
텍스트 파일에 작성 또는 쓰기/추가 (0) | 2022.09.23 |
MySQL의 부울 값에 대한 부울 vs tinyint(1) (0) | 2022.09.23 |
최대 콜 스택사이즈가 에러를 넘었습니다. (0) | 2022.09.23 |
Django ModelForm에서 ForeignKey 선택을 필터링하려면 어떻게 해야 합니까? (0) | 2022.09.23 |