在Java中解压缩到TreeMap中

我的问题相对简单.有没有人知道一个能够将压缩文件结构解压缩到
Java的TreeMap(或类似的可迭代结构)的免费库(LGPL)?

重点是,我可以自己做,但我不想重新发明已经很好的骑行轮:)

提前致谢!

所以我的问题是,我有一个zip文件,包含多个文件和目录,可能包含更多文件.我正在寻找的是一种方便的方法,如何将这个树结构提取到一个对象图中,无论它是否是一个TreeMap.例如:一个HashMap:{‘root’=> ‘HashMap:{‘file1.png’=> byte [] content}}

最佳答案

So my thing is, that i have a zip file, containing multiple files and directories which may contain more files.
What i am looking for is a convinient way how to extract this tree structure into an object graph, whether it’s a TreeMap or not.
So for example: a HashMap : {‘root’, ‘HashMap : {‘file1.png’ => byte[] content}}

As I answered some time ago在另一个问题上,Java API中没有单一的“树”数据结构(树形接口),因为每次使用都需要其他功能.
例如,您提出的HashMap树无法以类型保存的方式实现 – 您需要在某处使用包装器对象.

我不知道在某个地方是否已经存在一个zip文件的树状视图,(不)回答你的问题,但是一旦你定义了你想要的树界面就很难创建.

所以,这是一个示例类,它可以满足您的需求(根据我的理解).

import java.io.*;
import java.util.*;
import java.util.zip.*;

/**
 * A immutable wrapper around {@link ZipEntry} allowing
 * simple access of the childs of directory entries.
 */
public class ZipNode {

    private ZipNode parent;
    private Map<String,ZipNode> children;
    private boolean directory;

    /**
     * the corresponding Zip entry. If null, this is the root entry.
     */
    private ZipEntry entry;

    /** the ZipFile from where the nodes came. */
    private ZipFile file;


    private ZipNode(ZipFile f, ZipEntry entry) {
        this.file = f;
        this.entry = entry;
        if(entry == null || entry.isDirectory()) {
            directory = true;
            children = new LinkedHashMap<String, ZipNode>();
        }
        else {
            directory = false;
            children = Collections.emptyMap();
        }
    }

    /**
     * returns the last component of the name of
     * the entry, i.e. the file name. If this is a directory node,
     * the name ends with '/'. If this is the root node, the name
     * is simply "/".
     */
    public String getName() {
        if(entry == null)
            return "/";
        String longName = entry.getName();
        return longName.substring(longName.lastIndexOf('/',
                                                       longName.length()-2)+1);
    }

    /**
     * gets the corresponding ZipEntry to this node.
     * @return {@code null} if this is the root node (which has no
     *    corresponding entry), else the corresponding ZipEntry.
     */
    public ZipEntry getEntry() {
        return entry;
    }

    /**
     * Gets the ZipFile, from where this ZipNode came.
     */
    public ZipFile getZipFile() {
        return file;
    }

    /**
     * returns true if this node is a directory node.
     */
    public boolean isDirectory() {
        return directory;
    }

    /**
     * returns this node's parent node (null, if this is the root node).
     */
    public ZipNode getParent() {
        return parent;
    }

    /**
     * returns an unmodifiable map of the children of this node,
     * mapping their relative names to the ZipNode objects.
     * (Names of subdirectories end with '/'.)
     * The map is empty if this node is not a directory node.
     */
    public Map<String,ZipNode> getChildren() {
        return Collections.unmodifiableMap(children);
    }

    /**
     * opens an InputStream on this ZipNode. This only works when
     * this is not a directory node, and only before the corresponding
     * ZipFile is closed.
     */
    public InputStream openStream()
        throws IOException
    {
        return file.getInputStream(entry);
    }

    /**
     * a string representation of this ZipNode.
     */
    public String toString() {
        return "ZipNode [" + entry.getName() + "] in [" + file.getName() + "]";
    }



    /**
     * creates a ZipNode tree from a ZipFile
     * and returns the root node.
     *
     * The nodes' {@link #openStream()} methods are only usable until the
     * ZipFile is closed, but the structure information remains valid.
     */
    public static ZipNode fromZipFile(ZipFile zf) {
        return new ZipFileReader(zf).process();
    }


    /**
     * Helper class for {@link ZipNode#fromZipFile}.
     * It helps creating a tree of ZipNodes from a ZipFile.
     */
    private static class ZipFileReader {
        /**
         * The file to be read.
         */
        private ZipFile file;

        /**
         * The nodes collected so far.
         */
        private Map<String, ZipNode> collected;

        /**
         * our root node.
         */
        private ZipNode root;

        /**
         * creates a new ZipFileReader from a ZipFile.
         */
        ZipFileReader(ZipFile f) {
            this.file = f;
            this.collected = new HashMap<String, ZipNode>();
            collected.put("", root);
            root = new ZipNode(f, null);
        }

        /**
         * reads all entries, creates the corresponding Nodes and
         * returns the root node.
         */
        ZipNode process() {
            for(Enumeration<? extends ZipEntry> entries = file.entries();
                entries.hasMoreElements(); ) {
                this.addEntry(entries.nextElement());
            }
            return root;
        }

        /**
         * adds an entry to our tree.
         *
         * This may create a new ZipNode and then connects
         * it to its parent node.
         * @returns the ZipNode corresponding to the entry.
         */
        private ZipNode addEntry(ZipEntry entry) {
            String name = entry.getName();
            ZipNode node = collected.get(name);
            if(node != null) {
                // already in the map
                return node;
            }
            node = new ZipNode(file, entry);
            collected.put(name, node);
            this.findParent(node);
            return node;
        }

        /**
         * makes sure that the parent of a
         * node is in the collected-list as well, and this node is
         * registered as a child of it.
         * If necessary, the parent node is first created
         * and added to the tree.
         */
        private void findParent(ZipNode node) {
            String nodeName = node.entry.getName();
            int slashIndex = nodeName.lastIndexOf('/', nodeName.length()-2);
            if(slashIndex < 0) {
                // top-level-node
                connectParent(root, node, nodeName);
                return;
            }
            String parentName = nodeName.substring(0, slashIndex+1);
            ZipNode parent = addEntry(file.getEntry(parentName));
            connectParent(parent, node, nodeName.substring(slashIndex+1));
        }

        /**
         * connects a parent node with its child node.
         */
        private void connectParent(ZipNode parent, ZipNode child,
                                   String childName) {
            child.parent = parent;
            parent.children.put(childName, child);
        }


    }  // class ZipFileReader

    /**
     * test method. Give name of zip file as command line argument.
     */
    public static void main(String[] params)
        throws IOException
    {
        if(params.length < 1) {
            System.err.println("Invocation:  java ZipNode zipFile.zip");
            return;
        }
        ZipFile file = new ZipFile(params[0]);
        ZipNode root = ZipNode.fromZipFile(file);
        file.close();
        root.printTree("", " ", "");
    }

    /**
     * prints a simple tree view of this ZipNode.
     */
    private void printTree(String prefix,
                           String self,
                           String sub) {
        System.out.println(prefix + self + this.getName());
        String subPrefix = prefix + sub;
        // the prefix strings for the next level.
        String nextSelf = " ├─ ";
        String nextSub =  " │ ";
        Iterator<ZipNode> iterator =
            this.getChildren().values().iterator();
        while(iterator.hasNext()) {
            ZipNode child = iterator.next();
            if(!iterator.hasNext() ) {
                // last item, without the "|"
                nextSelf = " ╰─ ";
                nextSub =  "   ";
            }
            child.printTree(subPrefix, nextSelf, nextSub);
        }
    }
}

它有一个主要的测试方法,我的一个jar文件的输出是这样的:

 /
 ├─ META-INF/
 │  ╰─ MANIFEST.MF
 ╰─ de/
    ╰─ fencing_game/
       ├─ start/
       │  ├─ Runner.class
       │  ├─ ServerMain$1.class
       │  ├─ ServerMain.class
       │  ╰─ package-info.class
       ├─ log/
       │  ├─ Log$1.class
       │  ├─ Log.class
       │  ├─ LogImplClient.class
       │  ├─ Loggable.class
       │  ╰─ package-info.class
       ╰─ tools/
          ╰─ load/
             ├─ ServiceTools$1.class
             ├─ ServiceTools$2.class
             ├─ ServiceTools$3.class
             ├─ ServiceTools.class
             ╰─ TwoParentClassLoader.class

(但是,你需要一个支持unicode的终端和System.out的Unicode编码.)

点赞