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

时间:2015-02-20 15:13:26

标签: java deployment io nio

我有许多资源文件要复制到另一个位置作为方法调用。关于如何从JAR读取/复制文件,有各种各样的方法和指南以及问题。但是,如果我没有完全关闭,那么当从部署的JAR运行代码时,standard ways of getting a resource将查看不同的位置,而在开发/调试时从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);
 }

7 个答案:

答案 0 :(得分:3)

我建议你试试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);

答案 1 :(得分:1)

您可以使用

获取资源的位置
ExportUtils.class.getProtectionDomain().getCodeSource().getLocation()

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

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

答案 2 :(得分:0)

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

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循环所在的方法。

答案 3 :(得分:0)

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

}

答案 4 :(得分:0)

<强> RE-修改

您可以通过创建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;
    }
}

答案 5 :(得分:0)

要获取所有文件的列表,我使用以下实现。 这适用于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");

答案 6 :(得分:0)

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

相关问题