简体   繁体   中英

How to store each file in a zip archive, in an array using Java?

Ideally, I want to just read the zip archive and store each of the files in an Iterable ready to be compiled. (assume the zip only contains .java files)

Something like this would be good, but this won't work as I can't get the path of each file in the zip.

private ArrayList<String> javaFilePaths = new ArrayList<String>();
Iterable<? extends JavaFileObject> = fileManager.getJavaFileObjectsFromStrings(javaFilePaths);

Is it possible to have each element of the ArrayList to be the path to a file in the archive? So far I've only been able to go through the zip archive and get all the filenames which is useless as when I call getJavaFileObjectsFromStrings(javaFilePaths); it will not be the full path to the file. Maybe I need to store the content of each file in a new JavaFileObject first? Really not sure of the best way to go about this...

Can someone please help with this, it's proving very tricky!

UPDATE:

Thinking maybe of storing each file in the zip archive as a File object in an array of type File

Then calling the getJavaFileObjectsFromFiles(arrayOfFiles); method.

Solutions, ideas?

I would have commented on your answer as you requested but comments are limited in length. It is possible to achieve what you want: compiling the source without actually extracting it to a physical file on the file system.

quoting from the API of the interface StandardJavaFileManager

This file manager creates file objects representing regular files, zip file entries , or entries in similar file system based containers

searching in google I found the following article: Create dynamic applications with javax.tools

in the section "Java compilation: Concepts and implementation" it states the following on "source files":

one or more .java source files to compile. JavaFileManager provides an abstract file system that maps source and output file names to JavaFileObject instances. ( Here, file means an association between a unique name and a sequence of bytes. The client doesn't need to use an actual file system .)

The following article also does the same: Dynamic in-memory compilation

Both use CharSequence as the source to compile. You can try to build your own implementation based on the explanation in the articles above with the change of being based on ZipEntry. otherwise, you can read the contents of the zip entry (as you mentioned, you know its a java source file) into a StringBuffer and just use the implementation from one of the articles to compile it.

You could scan the ZIP file and keep the references to its entries. Then select the entry from the list, extract the file and do whatever you need to do with it. It is not necessary to fully read all files initially. One drawback: You must not call ZipFile.close() until you are finished with processing the ZIP archive.

For example:

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

public class SO {
    static class ZipFileRecord {
        final ZipFile zf;
        final ZipEntry ze;

        public ZipFileRecord(ZipFile zf, ZipEntry ze) {
            super();
            this.zf = zf;
            this.ze = ze;
        }

        public String content() throws IOException {
            InputStream is = zf.getInputStream(ze);
            BufferedReader br = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
            StringBuilder sb = new StringBuilder();
            String line = null;
            int count = 5;  // limit loop for reduced output
            while ((line = br.readLine())!=null) {
                sb.append(line);
                sb.append("\n");
                if (count--<=0) {
                    sb.append("...\n");
                    break;
                }
            }
            br.close();
            return sb.toString();
        }
    }
    public static class ZipWalker {
        final ArrayList<ZipFileRecord> list = new ArrayList<>();
        ZipFile zf;

        public void scan(String fn) throws ZipException, IOException {
            File f = new File(fn);
            if (!f.exists()) throw new RuntimeException("File not found: " + f);
            zf = new ZipFile(f);
            Enumeration<? extends ZipEntry> entries = zf.entries();
            for (ZipEntry ze : Collections.list(entries)) {
                if (ze.getName().endsWith(".java")) {
                    list.add(new ZipFileRecord(zf, ze));
                }
            }
        }

        public void dump() throws IOException {
            int count = 3;  // limit loop for reduced output
            for (ZipFileRecord fr : list) {
                System.out.println("=============> " + fr.ze);
                System.out.println(fr.content());
                if (count--<=0)
                    break;
            }
            zf.close();
        }
    }
    public static void main(String[] args) throws ZipException, IOException {
        ZipWalker zw = new ZipWalker();
        zw.scan("path/to/your/file.zip");
        zw.dump();
    }
}

Noteworthy things:

  1. The scan method does the scanning, the dump method represents all the other things, which you want to do with the files.
  2. ZipFile.close() must be called after you are finished with the ZIP file. You can't read data from its entries, if close() has been called.
  3. The count variables in the code are for output brevity - you need to remove those and the associated if s.
  4. For simplicity I used Reader and UTF-8 . If you present the file content to the user, you may have to deal with the file's charset. You may also have to deal with it for compilation.

I'm almost there, it works however the FileOutputStream creates the file, I could simply delete it after, but is there a way to do this without the creation of the file, and instead let it be virtual? Comment on my answer please.

btnBrowse.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int returnVal = fileChooser.showOpenDialog(null);
                if (returnVal == JFileChooser.APPROVE_OPTION) {
                    try {
                        isZip = true;
                        ZipFile zipFile = new ZipFile(fileChooser.getSelectedFile());
                        Enumeration<?> enu = zipFile.entries();
                        while (enu.hasMoreElements()) {
                            ZipEntry zipEntry = (ZipEntry) enu.nextElement();
                            File file = new File(zipEntry.getName());
                            InputStream is = zipFile.getInputStream(zipEntry);
                            FileOutputStream fos = new FileOutputStream(file);
                            byte[] bytes = new byte[1024];
                            int length;
                            while ((length = is.read(bytes)) >= 0) {
                                fos.write(bytes, 0, length);
                            }
                            is.close();
                            fos.close();
                            fileArrayList.add(file);
                        }
                        zipFile.close();
                    } catch (IOException ioe) {
                        ioe.printStackTrace();
                    }
                }
            }
        });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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