繁体   English   中英

是否可以在运行时复制所有资源文件(而无需专门命名)?

[英]Is it possible to copy all resource files (without naming them specifically) at runtime?

我有许多资源文件要复制到另一个位置作为方法调用。 关于如何从JAR读取/复制文件,有各种各样的方法和指南以及问题。 但是,如果我没有完全关闭,那么获取资源标准方法将在从部署的JAR运行代码时查看不同的位置,与在开发/调试时从Eclipse运行代码时相比。 因此适用于一个案例的解决方案不适用于另一个案例。

我当前和非常详细的解决方案(见下文)有效,但维护(添加或删除其他文件)是丑陋,冗长和烦人的。 有没有办法我可以优化这个方法,(可能是来自NIO包的新voodoo),例如只需循环遍历/res文件夹中的文件,然后将它们全部复制到新位置。 我不确定如何避免指定getResource()的文件名和路径在该场景中工作。

private void loadRes() throws IOException{
    // get styling resources from the jar file 
    File htmlRes = new File(topfolder, "res");
    if(!htmlRes.exists() || !htmlRes.isDirectory())
            htmlRes.mkdir();

    File cssfile = new File(htmlRes, "style.css"),
         bullet_file = new File(htmlRes,"i.gif"), 
         banner_file = new File(htmlRes, "banner.png"),
         bg_file = new File(htmlRes,"body_bg.png"),
         img_bg = new File(htmlRes,"button-bg.png"),
         hov_bg = new File(htmlRes,"button-hover-bg.png"),
         visu_img = new File(htmlRes,"visu.png"),
         pc_img = new File(htmlRes,"pc.png"),
         asc_gif = new File(htmlRes,"asc.gif"),
         desc_gif = new File(htmlRes,"desc.gif"),
         bg_gif = new File(htmlRes,"bg.gif");

    URL css_url = ExportUtils.class.getResource("/res/style.css");
    URL bullet_url = ExportUtils.class.getResource("/res/i.gif");
    URL banner_url = ExportUtils.class.getResource("/res/banner.png");
    URL bg_url = ExportUtils.class.getResource("/res/body_bg.png");
    URL image1_url = ExportUtils.class.getResource("/res/button-bg.png");
    URL image2_url = ExportUtils.class.getResource("/res/button-hover-bg.png");
    URL visu_url = ExportUtils.class.getResource("/res/visu.png");
    URL pc_url = ExportUtils.class.getResource("/res/pc.png");
    URL asc_url = ExportUtils.class.getResource("/res/asc.gif");
    URL desc_url = ExportUtils.class.getResource("/res/desc.gif");
    URL bggif_url = ExportUtils.class.getResource("/res/bg.gif");

    FileOutputStream fos = new FileOutputStream(cssfile);
    Resources.copy(css_url, fos);
    fos = new FileOutputStream(bullet_file);
    Resources.copy(bullet_url, fos);
    fos = new FileOutputStream(banner_file);
    Resources.copy(banner_url, fos);
    fos = new FileOutputStream(bg_file);
    Resources.copy(bg_url, fos);
    fos = new FileOutputStream(img_bg);
    Resources.copy(image1_url, fos);
    fos = new FileOutputStream(hov_bg);
    Resources.copy(image2_url, fos);
    fos = new FileOutputStream(visu_img);
    Resources.copy(visu_url, fos);
    fos = new FileOutputStream(pc_img);
    Resources.copy(pc_url, fos);
    fos = new FileOutputStream(asc_gif);
    Resources.copy(asc_url, fos);
    fos = new FileOutputStream(desc_gif);
    Resources.copy(desc_url, fos);
    fos = new FileOutputStream(bg_gif);
    Resources.copy(bggif_url, fos);

    // scripts and finish head
    URL sort_script = ExportUtils.class.getResource("/res/scripts.js");
    URL func_script = ExportUtils.class.getResource("/res/jquery.tablesorter.min.js");
    URL scinot_parser = ExportUtils.class.getResource("/res/jquery.tablesorter.scinot.js");

    File div_js = new File(htmlRes,"scripts.js");
    File jquery_sorttable = new File(htmlRes,"jquery.tablesorter.min.js");
    File jquery_st_scinot = new File(htmlRes, "jquery.tablesorter.scinot.js");

    fos = new FileOutputStream(div_js);
    Resources.copy(sort_script, fos);

    fos = new FileOutputStream(jquery_sorttable);
    Resources.copy(func_script, fos);

    fos = new FileOutputStream(jquery_st_scinot);
    Resources.copy(scinot_parser, fos);
 }

我建议你试试Google Reflections 它是一个旨在简化Java反射使用的库,但事实证明在类路径中查找资源同样很好。 要解决您的具体问题,您可以先在/ res usgin中获取以下代码中的所有资源:

Reflections reflections = new Reflections("res");
Set<String> resources = reflections.getResources(Pattern.compile(".*\\.*"));

然后,你需要得到每个资源的内容资源作为InputStream:

InputStream resourceInputSteam =  ClassLoader.class.getResourceAsStream(aResource);

最后将资源放入一个文件夹中,您可以使用Apache commons-io的IOUtils #copy轻松完成:

IOUtils.copy(resourceInputSteam, destinationFileOutputStream);

您可以使用获取资源的位置

ExportUtils.class.getProtectionDomain().getCodeSource().getLocation()

如果您在Eclipse中运行,则应该为您提供一个file:指向项目输出文件夹的URL。 如果从Jar运行它应该指向Jar。 然后,您可以从文件夹或JarInputStream迭代资源目录中的所有文件。

但是,由于这种方法在很大程度上取决于执行环境(例如类加载器和安全管理器),这可能会或可能不会起作用。 因此,我不建议在生产中使用它。 相反,我会使用您的方法,可能通过在顶部定义资源列表然后在循环中处理它们来减少代码重复,从而整理它。

我通常使用类文件的目录并扫描它们。

File directory = MyClass.class.getResource("/."); // or "." dont remember it

现在你可以使用了

for(File file : directory.listFiles()) { if(file.isDirectory()) scanDirectory(file); else { //check for resources and copy them } }

因此,您最终可以从类路径条目开始遍历目录树。 但检查包装等包装。 在罐子里面可能会变得更加棘手。

PS:scanDirectory是for循环所在的方法。

private void loadRes() throws IOException{
    DataInputStream dis  = null;
    FileOutputStream fos  = null;
    try{
        File htmlRes = new File(".", "res");
    if(!htmlRes.exists() || !htmlRes.isDirectory())
        htmlRes.mkdir();

        File outfile = new File(htmlRes, "/res/style.css");
        fos = new FileOutputStream(outfile);
        InputStream is =  Test.class.getResourceAsStream("/res/style.css");
        dis = new DataInputStream(is);
        while (true){
            fos.write(dis.readByte());
        }
//repeat for rest of the files
// or set up an array of file names and iterate this in a loop 

    }catch(NullPointerException npe){
        System.err.println("NullPointerException : " + npe.getMessage();
}catch(EOFException eofe){
        try{
                //This ensures the streams are closed properly
                dis.close();
                fos.close();
        }catch(IOException ioe){
                System.err.println("IOException: " + ioe.getMessage());
        }
    }catch(MalformedURLException murle){
            System.err.println("MalformedURLException: " + murle.getMessage());
    }catch(IOException ioe){
            System.err.println("IOException: " + ioe.getMessage());
    }

}

RE-EDIT

您可以通过创建Jar文件系统并查找其根目录来浏览jar文件。 然后FileVistor模式可用于获取jar文件中的每个文件。 对于另一种情况,根目录只是文件所在的目录。

使用getResource()告诉您是从jar文件还是目录加载类。 必须将输出操作为以后方法可以使用的形式。

public class JarFs extends SimpleFileVisitor<Path> {
    public static void main(String[] args) throws IOException {

        URL resource = JarFs.class.getResource("JarFs.class");
        System.out.println(resource);
        boolean isJar = false;
        String jarFile;
        if( resource.toString().startsWith("jar")) {
            isJar = true;
            jarFile = resource.toString().replaceAll("!.*", "").replace("file:", "file://");
        } else {
            isJar = false;
            jarFile = resource.toString().replace("file:", "").replace("JarFs.class", "");
        }
        JarFs j = new JarFs(jarFile, isJar);
        j.browse();
    }
    Path root;
    JarFs(String jar, boolean isJar)  throws IOException {
        if(isJar) {
            //If the class is loaded from a jar file, create a Jar filesystem.
            String path = jar;
            System.err.println("Create " + path);
            URI uri = URI.create(path);
            Map<String, ?> env = Collections.emptyMap();
            FileSystem fs = FileSystems.newFileSystem(uri, env);
            Iterable<Path> roots = fs.getRootDirectories();
            for (Path p : roots) {
                root = p;
                break;
            }
        } else {
            //while if the class is loaded from a file, use the directory it is loaded from 

            root = FileSystems.getDefault().getPath(jar);
        }
    }

设置完成后,您可以像这样遍历文件系统或目录

    void browse() throws IOException {
        Files.walkFileTree(root, this);
    }

支付是在visitFile()方法中

    @Override
    public FileVisitResult visitFile(Path file,
                               BasicFileAttributes attr) {
        if (attr.isRegularFile()) {
            // Do the business here
        }
        return FileVisitResult.CONTINUE;
    }
}

要获取所有文件的列表,我使用以下实现。 这适用于eclipse开发期间以及jar文件中的开发。

(代码使用Lombok,所以你必须用'真实'类型替换val ,而不是@Cleanup close()JarFile

public final class ResourceUtils {

  public static List<String> listFiles(Class<?> clazz, String path) throws IOException {
    val files = new ArrayList<String>();

    val url = clazz.getResource(path);
    if (url == null) throw new IllegalArgumentException("list files failed for path '" + path + "'.");

    if ("file".equals(url.getProtocol())) {
      try {
        val rootDir = new File(url.toURI());
        findFilesRecursive(files, rootDir, rootDir);
        return files;
      }
      catch (URISyntaxException e) {
        throw new AssertionError(e); // should never happen
      }
    }

    if ("jar".equals(url.getProtocol())) {
      if (path.startsWith("/")) path = path.substring(1);
      val jarFile = url.getPath().substring(5, url.getPath().indexOf("!"));
      @Cleanup val jar = new JarFile(jarFile);
      val entries = jar.entries();
      while (entries.hasMoreElements()) {
        val entry = entries.nextElement();
        if (!entry.isDirectory()) {
          val name = entry.getName();
          if (name.startsWith(path)) files.add(name.substring(path.length() + 1));
        }
      }

      return files;
    }

    throw new IllegalArgumentException("Unexpected protocol: " + url.getProtocol());
  }

  private static void findFilesRecursive(List<String> files, File rootDir, File currPath) {
    if (currPath.isDirectory()) {
      for (File path : currPath.listFiles()) {
        findFilesRecursive(files, rootDir, path);
      }
    }
    else {
      files.add(currPath.getAbsolutePath().substring(rootDir.getAbsolutePath().length() + 1));
    }
  }
}

像这样称呼它:

ResourceUtils.listFiles(MyClass.class, "/res");

您可以从环境中grep 类路径 这将允许您在定义的位置解压缩jar文件并过滤zip文件条目,除了.class所有内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM