سؤال

لدي هذا الكود الذي يقرأ جميع الملفات من دليل.

    File textFolder = new File("text_directory");

    File [] texFiles = textFolder.listFiles( new FileFilter() {
           public boolean accept( File file ) {
               return file.getName().endsWith(".txt");
           }
    });

يعمل كبيرة.يملأ مجموعة مع جميع الملفات التي تنتهي ".txt" من الدليل "text_directory".

كيف يمكن قراءة محتويات الدليل بطريقة مماثلة داخل جرة الملف ؟

ذلك ما كنت حقا تريد القيام به هو أن قائمة جميع الصور داخل ملف 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... */
}

علما أنه في جافا 7 ، يمكنك إنشاء FileSystem من جرة (zip) الملف ثم استخدام NIO دليل المشي و تصفية آليات البحث من خلال ذلك.هذا من شأنه أن يجعل من الأسهل لكتابة التعليمات البرمجية التي تعالج الجرار و "انفجرت" الدلائل.

نصائح أخرى

الكود يعمل على كل IDE و .جرة الملفات:

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 هو الجواب, إذ هو غير آمنة الحل لعدة أسباب:

  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 واجهة على نفس الملفات و يمكن أن تتداخل معها (حتى في واحدة مترابطة التطبيق).
كما أنها لا تحقق nulls (على سبيل المثال ، getClass().getResource().

هذا خاص جافا NIO واجهة نوع الرهيبة ، لأنه يدخل عالمي/المفرد غير مؤشر الترابط-الآمن الموارد ، الوثائق غامضة للغاية (الكثير من المجاهيل بسبب مزود تطبيقات محددة).قد تختلف النتائج الأخرى FileSystem مقدمي (لا جرة).ربما هناك سبب وجيه لذلك يجري بهذه الطريقة ، أنا لا أعرف, أنا لم بحثت تطبيقات.

وهنا طريقة كتبتها "تشغيل جميع 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);
    }
}

لذلك أعتقد أن المشكلة الرئيسية سيكون, كيف تعرف اسم جرة حيث الرئيسية في حياة الطبقة.

على افتراض أن المشروع هي معبأة في وعاء (ليس بالضرورة صحيحا!), يمكنك استخدام ClassLoader.getResource() أو findResource() مع اسم الفئة (تليها .الطبقة) للحصول على الجرة التي تحتوي على فئة معينة.عليك أن تحليل جرة اسم من عنوان URL الذي يحصل عاد (ليس صعبة) ، والتي سوف أترك باعتبارها ممارسة للقارئ :-)

تأكد من اختبار حال فئة ليست جزء من جرة.

جرة الملف هو ملف مضغوط مع منظم واضح.يمكنك فتح ملف jar مع المعتاد جافا الرمز البريدي أدوات مسح محتويات الملف بهذه الطريقة تضخيم تيارات ، إلخ.ثم استخدام ذلك في getResourceAsStream الدعوة ، وأنه ينبغي أن يكون كل شيء جيد المظهر

تحرير / بعد التوضيح

استغرق مني لحظة أن تذكر جميع أجزاء و أنا متأكد من أن هناك نظافة الطرق للقيام بذلك ، ولكن أردت أن أرى أنني لم أكن مجنون.في مشروعي image.jpg هو ملف في جزء من جرة الرئيسية الملف.أحصل على محمل فئة من الطبقة الرئيسية (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

نظرا الفعلي ملف JAR, يمكنك قائمة المحتويات باستخدام JarFile.entries().سوف تحتاج إلى معرفة موقع ملف JAR الرغم من ذلك - لا يمكنك فقط أن تطلب classloader إلى قائمة كل شيء يمكن أن تحصل في.

يجب أن تكون قادرة على العمل خارج موقع ملف JAR على أساس URL عاد من ThisClassName.class.getResource("ThisClassName.class"), ولكن قد يكون قليلا صغيرة ذكي.

منذ بعض الوقت لقد قمت الوظيفة التي يحصل classess من داخل جرة:

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

هنا هو مثال على استخدام تأملات المكتبة بشكل متكرر مسح classpath قبل 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);
}

يعمل هذا مع كل الجرار انفجرت الطبقات.

لقد استدار acheron55 الجواب جافا 7 و أغلقت FileSystem الكائن.هذا الكود يعمل في IDE في جرة الملفات في وعاء داخل الحرب على هر 7;لكن لاحظ أنه لا لا العمل في جرة داخل الحرب على جبوس 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;
                }
            });
        }
    }
}

هناك نوعان من المفيد جدا المرافق كل من دعا JarScan:

  1. www.inetfeedback.com/jarscan

  2. jarscan.dev.java.net

انظر أيضا هذا السؤال: JarScan مسح جميع الملفات جرة في جميع المجلدات الفرعية على فئة معينة

مجرد وسيلة مختلفة من القائمة/قراءة الملفات من جرة URL يفعل ذلك بشكل متكرر المتداخلة الجرار

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 
    }
});
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top