Android добавляет файлы в zip-файл без необходимости перезаписывать весь zip-файл?

Как я могу добавить файлы к существующему zip-файлу? У меня уже есть код для создания zip-файла, и он отлично работает, за исключением одной большой проблемы. Как это работает сейчас, пользователь делает несколько снимков, и в конце все снимки добавляются в zip-файл, что может занять довольно много времени, если вы сделаете достаточно снимков. :-( Итак, я думаю, у меня есть очень хорошее и эффективное решение. Когда будут сделаны снимки, я просто добавлю каждое новое изображение в zip-файл сразу после того, как оно было снято. Затем, когда они закончат делать снимки, доработайте zip-файл, чтобы его можно было использовать, и экспортируйте его. :-)

Проблема в том, что я не могу заставить его добавлять файлы в существующий zip-файл. :-( Вот что у меня есть на данный момент. Также имейте в виду, это всего лишь доказательство концепции, я понимаю, что повторная инициализация всего для каждой итерации цикла for очень глупая. Каждая итерация цикла Предполагается, что добавляется еще один файл, который, скорее всего, будет через много времени, может быть, даже через час, поэтому у меня все сбрасывает каждую итерацию, потому что приложение будет закрыто между добавлением файлов.Если я смогу заставить это работать , то я фактически откажусь от цикла for и вставлю этот код в функцию, которая вызывается каждый раз, когда делается снимок. :-)

        try  {
            for(int i=0; i < _files.size(); i++) {
                //beginning of initial setup stuff
                BufferedInputStream origin = null;
                FileOutputStream dest = new FileOutputStream(_zipFile,false); 
                ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
                byte data[] = new byte[BUFFER];
                out.setLevel(0); //I added this because it makes it not compress the data 
                //at all and I hoped that it would allow the zip to be appended to
                //end of initial setup stuff

                //beginning of old for loop
                Log.v("Compress", "Adding: " + _files[i]); 
                FileInputStream fi = new FileInputStream(_files[i]); 
                origin = new BufferedInputStream(fi, BUFFER); 
                ZipEntry entry = new ZipEntry(_files[i].substring(_files[i].lastIndexOf("/") + 1)); 
                out.putNextEntry(entry); 
                int count; 
                while ((count = origin.read(data, 0, BUFFER)) != -1) { 
                    out.write(data, 0, count); 
                } 
                origin.close();     
                //end of for old loop

                //beginning of finishing stuff
                out.close();
                //end of finishing stuff
            } 
        } catch(Exception e) {
            Log.e("ZipCreation", "Error writing zip", e);
            e.printStackTrace();
        }

Кроме того, я экспериментировал с

FileOutputStream dest = new FileOutputStream(_zipFile,true);

Если вы заметили, я установил для append значение true, что фактически добавит данные в существующий файл. И что интересно, он действительно добавляет данные в исходный файл, однако после того, как файл извлекается на моем компьютере, извлекается только последний записанный файл, что плохо. :-( Так есть ли способ начать писать zip-файл, а потом добавить к нему и закончить zip-файл? Я даже подумал о том, чтобы, возможно, взять ZipOutputStream и изменить его, чтобы он соответствовал этой модели, которая мне нужна . Это должно быть логически возможно как-нибудь? :-)

Заранее спасибо за помощь! :-D

-Джаред


person Jared    schedule 21.04.2011    source источник


Ответы (2)


Хорошо, спасибо за все ваши предложения, но мне удалось заставить его работать так, как я хотел .... это МОЖЕТ быть сделано, вы МОЖЕТЕ добавлять файлы после закрытия файла, пока вы сохраняете свое место !!! :-D

Вот как мне удалось заставить его работать:

        try  {
            for(int i=0; i < _files.size(); i++) {
                //beginning of initial setup stuff
                BufferedInputStream origin = null;
                FileOutputStream dest = new FileOutputStream(_zipFile,true); 
                ZipOutputStreamNew out = new ZipOutputStreamNew(new BufferedOutputStream(dest));
                byte data[] = new byte[BUFFER];
                if (havePreviousData) {
                    out.setWritten(tempWritten);
                    out.setXentries(tempXentries);
                }
                //end of initial setup stuff

                //beginning of for loop
                Log.i("Compress", "Adding: " + _files.get(i));
                FileInputStream fi = new FileInputStream(_files.get(i)); 
                origin = new BufferedInputStream(fi, BUFFER);
                TempString = _files.get(i).substring(_files.get(i).lastIndexOf("/") + 1);
                ZipEntry entry = new ZipEntry(_paths.get(i) + TempString);
                out.putNextEntry(entry);
                int count;
                while ((count = origin.read(data, 0, BUFFER)) != -1) { 
                    out.write(data, 0, count); 
                }
                origin.close();
                out.closeEntry();
                //end of for loop

                //beginning of finishing stuff
                if (i == (_files.size()-1)) {
                    //it's the last record so we should finish it off
                    out.closeAndFinish();
                } else {
                    //close the file, but don't write the Central Directory
                    //first, back up where the zip file was...
                    tempWritten = out.getWritten();
                    tempXentries = out.getXentries();
                    havePreviousData = true;
                    //now close the file
                    out.close();
                }
                //end of finishing stuff
            }
            //zip succeeded
    } catch(Exception e) {
        Log.e("ZipCreation", "Error writing zip", e);
        e.printStackTrace();
    }

Кроме того, имейте в виду, что это не единственный код, который мне пришлось сделать. Мне также пришлось сделать свою собственную копию ZipOutputStream, чтобы я мог предоставить следующие функции, которые я создал в своем классе ZipOutputStreamNew ....

getWritten()
getXentries()

а также

setWritten(long mWritten)
setXentries(Vector<XEntry> mXEntries)

По большей части, все это происходит, если он начинает писать как обычно, затем, вместо того, чтобы закрывать как обычно, он создает резервную копию этих двух переменных, а затем для следующей итерации восстанавливает только эти переменные.

Дайте мне знать, если у вас есть какие-либо вопросы обо всем этом, но я знал, что это сработает, все, что нужно сделать, это сохранить то, где это было. :-D

Еще раз спасибо за помощь всем! :-)

По просьбе Раджа, вот исходный код ZipOutputStreamNew:

/**
 * This class implements an output stream filter for writing files in the
 * ZIP file format. Includes support for both compressed and uncompressed
 * entries.
 *
 * @author  David Connelly
 * @version %I%, %G%
 */
public class ZipOutputStreamNew extends DeflaterOutputStream implements ZipConstants {
    public static class XEntry {
        public final ZipEntry entry;
        public final long offset;
        public final int flag;
        public XEntry(ZipEntry entry, long offset) {
            this.entry = entry;
            this.offset = offset;
            this.flag = (entry.getMethod() == DEFLATED &&
                 (entry.getSize() == -1 ||
                  entry.getCompressedSize() == -1 ||
                  entry.getCrc() == -1))
            // store size, compressed size, and crc-32 in data descriptor
            // immediately following the compressed entry data
            ? 8
            // store size, compressed size, and crc-32 in LOC header
            : 0;
        }
    }

    private XEntry current;
    private Vector<XEntry> xentries = new Vector<XEntry>();
    private HashSet<String> names = new HashSet<String>();
    private CRC32 crc = new CRC32();
    private long written = 0;
    private long locoff = 0;
    private String comment;
    private int method = DEFLATED;
    private boolean finished;

    private boolean closed = false;
    private boolean closeItPermanently = false;

    private static int version(ZipEntry e) throws ZipException {
    switch (e.getMethod()) {
    case DEFLATED: return 20;
    case STORED:   return 10;
    default: throw new ZipException("unsupported compression method");
    }
    }

    /**
     * Checks to make sure that this stream has not been closed.
     */
    private void ensureOpen() throws IOException {
    if (closed) {
        throw new IOException("Stream closed");
        }
    }
    /**
     * Compression method for uncompressed (STORED) entries.
     */
    public static final int STORED = ZipEntry.STORED;

    /**
     * Compression method for compressed (DEFLATED) entries.
     */
    public static final int DEFLATED = ZipEntry.DEFLATED;

    /**
     * Creates a new ZIP output stream.
     * @param out the actual output stream
     */
    public ZipOutputStreamNew(OutputStream out) {
    super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
        usesDefaultDeflater = true;
    }

    /**
     * Sets the ZIP file comment.
     * @param comment the comment string
     * @exception IllegalArgumentException if the length of the specified
     *        ZIP file comment is greater than 0xFFFF bytes
     */
    public void setComment(String comment) {
        if (comment != null && comment.length() > 0xffff/3
                                           && getUTF8Length(comment) > 0xffff) {
        throw new IllegalArgumentException("ZIP file comment too long.");
    }
    this.comment = comment;
    }

    /**
     * Sets the default compression method for subsequent entries. This
     * default will be used whenever the compression method is not specified
     * for an individual ZIP file entry, and is initially set to DEFLATED.
     * @param method the default compression method
     * @exception IllegalArgumentException if the specified compression method
     *        is invalid
     */
    public void setMethod(int method) {
    if (method != DEFLATED && method != STORED) {
        throw new IllegalArgumentException("invalid compression method");
    }
    this.method = method;
    }

    /**
     * Sets the compression level for subsequent entries which are DEFLATED.
     * The default setting is DEFAULT_COMPRESSION.
     * @param level the compression level (0-9)
     * @exception IllegalArgumentException if the compression level is invalid
     */
    public void setLevel(int level) {
    def.setLevel(level);
    }

    /**
     * Begins writing a new ZIP file entry and positions the stream to the
     * start of the entry data. Closes the current entry if still active.
     * The default compression method will be used if no compression method
     * was specified for the entry, and the current time will be used if
     * the entry has no set modification time.
     * @param e the ZIP entry to be written
     * @exception ZipException if a ZIP format error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void putNextEntry(ZipEntry e) throws IOException {
    ensureOpen();
    if (current != null) {
        closeEntry();   // close previous entry
    }
    if (e.getTime() == -1) {
        e.setTime(System.currentTimeMillis());
    }
    if (e.getMethod() == -1) {
        e.setMethod(method);    // use default method
    }
    switch (e.getMethod()) {
    case DEFLATED:
        break;
    case STORED:
        // compressed size, uncompressed size, and crc-32 must all be
        // set for entries using STORED compression method
        if (e.getSize() == -1) {
        e.setSize(e.getCompressedSize());
        } else if (e.getCompressedSize() == -1) {
        e.setCompressedSize(e.getSize());
        } else if (e.getSize() != e.getCompressedSize()) {
        throw new ZipException(
            "STORED entry where compressed != uncompressed size");
        }
        if (e.getSize() == -1 || e.getCrc() == -1) {
        throw new ZipException(
            "STORED entry missing size, compressed size, or crc-32");
        }
        break;
    default:
        throw new ZipException("unsupported compression method");
    }
    if (! names.add(e.getName())) {
        throw new ZipException("duplicate entry: " + e.getName());
    }
    current = new XEntry(e, written);
    xentries.add(current);
        writeLOC(current);
    }

    /**
     * Closes the current ZIP entry and positions the stream for writing
     * the next entry.
     * @exception ZipException if a ZIP format error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void closeEntry() throws IOException {
    ensureOpen();
    if (current != null) {
        ZipEntry e = current.entry;
        switch (e.getMethod()) {
        case DEFLATED:
        def.finish();
        while (!def.finished()) {
            deflate();
        }
        if ((current.flag & 8) == 0) {
            // verify size, compressed size, and crc-32 settings
            if (e.getSize() != def.getBytesRead()) {
            throw new ZipException(
                "invalid entry size (expected " + e.getSize() +
                " but got " + def.getBytesRead() + " bytes)");
            }
            if (e.getCompressedSize() != def.getBytesWritten()) {
            throw new ZipException(
                "invalid entry compressed size (expected " +
                e.getCompressedSize() + " but got " + def.getBytesWritten() + " bytes)");
            }
            if (e.getCrc() != crc.getValue()) {
            throw new ZipException(
                "invalid entry CRC-32 (expected 0x" +
                Long.toHexString(e.getCrc()) + " but got 0x" +
                Long.toHexString(crc.getValue()) + ")");
            }
        } else {
            e.setSize(def.getBytesRead());
            e.setCompressedSize(def.getBytesWritten());
            e.setCrc(crc.getValue());
            writeEXT(e);
        }
        def.reset();
        written += e.getCompressedSize();
        break;
        case STORED:
        // we already know that both e.size and e.csize are the same
        if (e.getSize() != written - locoff) {
            throw new ZipException(
            "invalid entry size (expected " + e.getSize() +
            " but got " + (written - locoff) + " bytes)");
        }
        if (e.getCrc() != crc.getValue()) {
            throw new ZipException(
             "invalid entry crc-32 (expected 0x" +
             Long.toHexString(e.getCrc()) + " but got 0x" +
             Long.toHexString(crc.getValue()) + ")");
        }
        break;
        default:
        throw new ZipException("invalid compression method");
        }
        crc.reset();
        current = null;
    }
    }

    /**
     * Writes an array of bytes to the current ZIP entry data. This method
     * will block until all the bytes are written.
     * @param b the data to be written
     * @param off the start offset in the data
     * @param len the number of bytes that are written
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public synchronized void write(byte[] b, int off, int len)
    throws IOException
    {
    ensureOpen();
        if (off < 0 || len < 0 || off > b.length - len) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return;
    }

    if (current == null) {
        throw new ZipException("no current ZIP entry");
    }
    ZipEntry entry = current.entry;
    switch (entry.getMethod()) {
    case DEFLATED:
        super.write(b, off, len);
        break;
    case STORED:
        written += len;
        if (written - locoff > entry.getSize()) {
        throw new ZipException(
            "attempt to write past end of STORED entry");
        }
        out.write(b, off, len);
        break;
    default:
        throw new ZipException("invalid compression method");
    }
    crc.update(b, off, len);
    }

    /**
     * Finishes writing the contents of the ZIP output stream without closing
     * the underlying stream. Use this method when applying multiple filters
     * in succession to the same output stream.
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O exception has occurred
     */
    public void finish() throws IOException {
        ensureOpen();
        if (finished) {
            return;
        }
        if (current != null) {
            closeEntry();
        }
        if (xentries.size() < 1) {
            throw new ZipException("ZIP file must have at least one entry");
        }
        if (closeItPermanently) {
            // write central directory
            long off = written;
            for (XEntry xentry : xentries)
                writeCEN(xentry);
            writeEND(off, written - off);
            finished = true;
            //Log.e("ZipOutputStreamNew", "I just ran wrote the Central Directory Jared!");
        }
        //Log.e("ZipOutputStreamNew", "I just ran finish() Jared!");
    }

    /**
     * Gets the value of the "xentries" variable (for later use)
     * @return
     */
    public Vector<XEntry> getXentries() {
        return xentries;

        //TODO convert this to primitive data types
    }

    /**
     * Gets the value of the "written" variable (for later use)
     * @return
     */
    public long getWritten() {
        return written;
    }


    /**
     * Sets the value of the "xentries" variable (for later use)
     * @return
     */
    public void setXentries(Vector<XEntry> mXEntries) {
        xentries = mXEntries;
        //TODO convert this to primitive data types
    }

    /**
     * Sets the value of the "written" variable (for later use)
     * @return
     */
    public void setWritten(long mWritten) {
        written = mWritten;
    }


    /**
     * Closes the ZIP output stream as well as the stream being filtered.
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void closeAndFinish() throws IOException {
        if (!closed) {
            closeItPermanently=true;
            super.close();
            closed = true;
        }
    }

    /**
     * Used to close the ZIP output stream as well as the stream being filtered.
     * instead it does nothing :-P
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void close() throws IOException {
        if (!closed) {
            closeItPermanently=false;
            super.close();
            closed = true;
        }
    }

    /*
     * Writes local file (LOC) header for specified entry.
     */
    private void writeLOC(XEntry xentry) throws IOException {
    ZipEntry e = xentry.entry;
    int flag = xentry.flag;
    writeInt(LOCSIG);       // LOC header signature
    writeShort(version(e));     // version needed to extract
    writeShort(flag);           // general purpose bit flag
    writeShort(e.getMethod());       // compression method
    writeInt(e.getTime());           // last modification time
    if ((flag & 8) == 8) {
        // store size, uncompressed size, and crc-32 in data descriptor
        // immediately following compressed entry data
        writeInt(0);
        writeInt(0);
        writeInt(0);
    } else {
        writeInt(e.getCrc());        // crc-32
        writeInt(e.getCompressedSize());      // compressed size
        writeInt(e.getSize());       // uncompressed size
    }
    byte[] nameBytes = getUTF8Bytes(e.getName());
    writeShort(nameBytes.length);
    writeShort(e.getExtra() != null ? e.getExtra().length : 0);
    writeBytes(nameBytes, 0, nameBytes.length);
    if (e.getExtra() != null) {
        writeBytes(e.getExtra(), 0, e.getExtra().length);
    }
    locoff = written;
    }

    /*
     * Writes extra data descriptor (EXT) for specified entry.
     */
    private void writeEXT(ZipEntry e) throws IOException {
    writeInt(EXTSIG);       // EXT header signature
    writeInt(e.getCrc());       // crc-32
    writeInt(e.getCompressedSize());        // compressed size
    writeInt(e.getSize());      // uncompressed size
    }

    /*
     * Write central directory (CEN) header for specified entry.
     * REMIND: add support for file attributes
     */
    private void writeCEN(XEntry xentry) throws IOException {
    ZipEntry e  = xentry.entry;
    int flag = xentry.flag;
    int version = version(e);
    writeInt(CENSIG);       // CEN header signature
    writeShort(version);        // version made by
    writeShort(version);        // version needed to extract
    writeShort(flag);       // general purpose bit flag
    writeShort(e.getMethod());      // compression method
    writeInt(e.getTime());      // last modification time
    writeInt(e.getCrc());       // crc-32
    writeInt(e.getCompressedSize());        // compressed size
    writeInt(e.getSize());      // uncompressed size
    byte[] nameBytes = getUTF8Bytes(e.getName());
    writeShort(nameBytes.length);
    writeShort(e.getExtra() != null ? e.getExtra().length : 0);
    byte[] commentBytes;
    if (e.getComment() != null) {
        commentBytes = getUTF8Bytes(e.getComment());
        writeShort(commentBytes.length);
    } else {
        commentBytes = null;
        writeShort(0);
    }
    writeShort(0);          // starting disk number
    writeShort(0);          // internal file attributes (unused)
    writeInt(0);            // external file attributes (unused)
    writeInt(xentry.offset);    // relative offset of local header
    writeBytes(nameBytes, 0, nameBytes.length);
    if (e.getExtra() != null) {
        writeBytes(e.getExtra(), 0, e.getExtra().length);
    }
    if (commentBytes != null) {
        writeBytes(commentBytes, 0, commentBytes.length);
    }
    }

    /*
     * Writes end of central directory (END) header.
     */
    private void writeEND(long off, long len) throws IOException {
    int count = xentries.size();
    writeInt(ENDSIG);       // END record signature
    writeShort(0);          // number of this disk
    writeShort(0);          // central directory start disk
    writeShort(count);      // number of directory entries on disk
    writeShort(count);      // total number of directory entries
    writeInt(len);          // length of central directory
    writeInt(off);          // offset of central directory
    if (comment != null) {      // zip file comment
        byte[] b = getUTF8Bytes(comment);
        writeShort(b.length);
        writeBytes(b, 0, b.length);
    } else {
        writeShort(0);
    }
    }

    /*
     * Writes a 16-bit short to the output stream in little-endian byte order.
     */
    private void writeShort(int v) throws IOException {
    OutputStream out = this.out;
    out.write((v >>> 0) & 0xff);
    out.write((v >>> 8) & 0xff);
    written += 2;
    }

    /*
     * Writes a 32-bit int to the output stream in little-endian byte order.
     */
    private void writeInt(long v) throws IOException {
    OutputStream out = this.out;
    out.write((int)((v >>>  0) & 0xff));
    out.write((int)((v >>>  8) & 0xff));
    out.write((int)((v >>> 16) & 0xff));
    out.write((int)((v >>> 24) & 0xff));
    written += 4;
    }

    /*
     * Writes an array of bytes to the output stream.
     */
    private void writeBytes(byte[] b, int off, int len) throws IOException {
    super.out.write(b, off, len);
    written += len;
    }

    /*
     * Returns the length of String's UTF8 encoding.
     */
    static int getUTF8Length(String s) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch <= 0x7f) {
                count++;
            } else if (ch <= 0x7ff) {
                count += 2;
            } else {
                count += 3;
            }
        }
        return count;
    }

    /*
     * Returns an array of bytes representing the UTF8 encoding
     * of the specified String.
     */
    private static byte[] getUTF8Bytes(String s) {
    char[] c = s.toCharArray();
    int len = c.length;
    // Count the number of encoded bytes...
    int count = 0;
    for (int i = 0; i < len; i++) {
        int ch = c[i];
        if (ch <= 0x7f) {
        count++;
        } else if (ch <= 0x7ff) {
        count += 2;
        } else {
        count += 3;
        }
    }
    // Now return the encoded bytes...
    byte[] b = new byte[count];
    int off = 0;
    for (int i = 0; i < len; i++) {
        int ch = c[i];
        if (ch <= 0x7f) {
        b[off++] = (byte)ch;
        } else if (ch <= 0x7ff) {
        b[off++] = (byte)((ch >> 6) | 0xc0);
        b[off++] = (byte)((ch & 0x3f) | 0x80);
        } else {
        b[off++] = (byte)((ch >> 12) | 0xe0);
        b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
        b[off++] = (byte)((ch & 0x3f) | 0x80);
        }
    }
    return b;
    }
}
person Jared    schedule 24.04.2011
comment
Просто быстрое обновление ... Я тестировал этот метод записи части zip-файла каждый раз, когда делается снимок, а затем, когда пользователь нажимает экспорт, просто закрывая zip-файл. После нескольких тестов все работает отлично! Каждый раз, когда вы делаете снимок, делается небольшая пауза, что не имеет большого значения. Ранее при обычном методе на запись всех файлов в zip-файл в конце уходило до нескольких минут. Но теперь с этим методом экспорт в конце происходит мгновенно !!!! :-D Спасибо, ребята! :-) - person Jared; 01.05.2011
comment
Я пробовал этот код, но моя проблема в том, как добавить мой файл в этот код и где получить zip-файл из этого кода. - person Android-iPhone-rahul; 28.11.2011
comment
В приведенном мною примере _files - это ListArray строк, которые, если вы посмотрите на цикл for, будут повторены и добавлены в zip-файл, который хранится в виде строки с именем _zipFile, поэтому zip-файл будет записан в то, что вы установили _zipFile к :-) - person Jared; 14.12.2011
comment
Кроме того, имейте в виду, что это больше написано как доказательство концепции, вы обычно не закрываете и не открываете FileOutputStream при каждой итерации цикла, однако мне нужно было иметь возможность это сделать, потому что я нужно было добавить файл в zip-архив, а затем добавить еще один файл, возможно, после перезапуска приложения, поэтому этот пример у меня так хорошо сработал :-) (надеюсь, он вам тоже поможет) :-) - person Jared; 14.12.2011
comment
И не забудьте сделать свою собственную копию класса ZipOutputStream, который, как мне кажется, можно получить из библиотек Java с открытым исходным кодом. По сути, вам нужно скопировать их класс, а затем добавить что-то, чтобы заставить его работать, как в этом примере. - person Jared; 14.12.2011
comment
Привет, @Jared, не могли бы вы предоставить код того, что именно делает ZipOutputStreamNew? .. Я столкнулся с той же проблемой; Установка для FileOutputStream значения «true» работает некорректно. Буду признателен, если вы дадите мне знать, что именно вы делаете в ZipOutputStreamNew (т.е. методы setWritten и setXentries) или фактический код ZipOutputStreamNew было бы замечательно. - person Raj; 03.01.2015
comment
Привет, Радж, я отредактировал свой ответ и добавил исходный код для ZipOutputStreamNew. Надеюсь, это тебе поможет :-) - person Jared; 05.01.2015

Я считаю, что это невозможно сделать прямо сейчас с текущим API.
Вы можете добавлять данные в любой файл, но это не означает, что вы получите правильный формат файла. Файл .zip не похож на файл .tar, и сжатие требует ограничений на обработку файлов (позиции файлов, EOF и т. Д.). Если учесть структуру формата файла (взято из Википедии здесь ) вы поймете, почему просто добавление не работает.

Есть библиотека под названием TrueZip, которая может работать, хотя я не знаю, поддерживает ли она android. Взгляните на этот ответ в другом аналогичном вопросе: Добавление файлы в zip-архив с помощью Java.

Кроме того, в качестве обходного пути вы можете создать отдельные .zip файлы и добавить их в виде архива (формат файла здесь). Сжатие может быть немного хуже, но с точки зрения экономии времени было бы намного лучше.


Обновление на основе комментариев (и возможное решение)

Вы можете отделить добавление от каждого ZipEntry и оставить объект ZipOutputStream открытым, пока вы все еще делаете снимки. Однако я вижу риски при таком подходе, поскольку любая проблема с приложением во время фотосъемки (принудительное закрытие, разряд батареи и т. Д.) Может сделать весь файл непригодным для использования. Вам нужно будет убедиться, что вы используете правильные блоки try / catch / finally, чтобы закрыть файл и вызвать closeZip() при таких событиях, как onClose() и onDestroy(), но идея будет следующая:

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

public class Zip {
    static final int BUFFER = 2048;

    ZipOutputStream out;
    byte data[];

    public Zip(String name) {
        FileOutputStream dest = new FileOutputStream(name);
        out = new ZipOutputStream(new BufferedOutputStream(dest));
        data = new byte[BUFFER];
    }

    public void addFile (String name) {
        FileInputStream fi = new FileInputStream(name);
        BufferedInputStream origin = new BufferedInputStream(fi, BUFFER);
        ZipEntry entry = new ZipEntry(name);
        out.putNextEntry(entry);
        int count;
        while((count = origin.read(data, 0, BUFFER)) != -1) {
           out.write(data, 0, count);
        }
        origin.close();
     }

    public void closeZip () {
        out.close();
    }
}
person Aleadam    schedule 21.04.2011
comment
На самом деле, я все еще думаю, что это можно сделать, прямо сейчас я изучаю возможность расширения класса ZipOutputStream и открытия некоторых свойств, чтобы я мог, возможно, выполнить их резервное копирование с помощью вызова функции, который я создаю в своем расширенном классе, а затем восстановить, где Я был. Все, что я говорю, должен быть способ. Основная концепция проста: начните запись файла, сделайте резервную копию с того места, где вы были, а затем возобновите запись файла позже. Должен быть способ сделать хотя бы что-то подобное. Я посмотрю, смогу ли я чего-нибудь добиться с расширением ZipOutputStream. Тем не менее, спасибо за совет по TrueZip. :-) - person Jared; 21.04.2011
comment
@Aleadam, посмотрев на формат zip-файла в Википедии, я еще сильнее чувствую, что нет причин, по которым я не смогу возобновить запись zip-файла позже. Пожалуйста, имейте в виду, я полагаю, что я неправильно пометил свой пост, извините, потому что я не хочу добавлять файл в zip-файл. Все, что мне нужно, это начать запись zip-файла, затем в какой-то момент приостановить запись, закрыть выходной поток без завершения zip-архива, сохранить все мои текущие переменные (которые будут включать информацию о центральном каталоге), а затем в позже загрузите эти переменные и продолжите с того места, где я остановился. :-) - person Jared; 23.04.2011
comment
@Aleadam, может быть, поскольку я действительно не хочу добавлять файлы, мой вопрос неправильно помечен, и, может быть, мне следует задать его еще раз после перефразирования некоторых вещей? Что вы думаете :-\ - person Jared; 23.04.2011
comment
Хорошо, я обновил свой ответ. Это далеко от полного класса, но идея в этом. В случае, если его пришлось закрыть посередине, вы в конечном итоге откроете, прочитаете его и переписываете все снова (не в примере), но если все работает нормально, это должно сработать. - person Aleadam; 23.04.2011
comment
@Aleadam, Спасибо за вашу помощь, но я смог сделать это, не считывая все данные, с помощью стратегии сохранения текущего состояния, которую я надеялся достичь. Все, что мне нужно было сделать, это обманом заставить его не писать центральный каталог, затем создать резервную копию, где мы были, а затем, в следующий раз, когда я добавил файл, я восстановил его из резервной копии. Тем не менее, спасибо за вашу помощь! :-) - person Jared; 24.04.2011