/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved. * * The contents of this file are subject to the terms of the GNU * General Public License Version 3 only ("GPL"). * You may not use this file except in compliance with the License. * You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html * See the License for the specific language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each file. * */ package org.jopendocument.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.Set; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipOutputStream; /** * Permet d'écrire dans un fichier zip. * * @author ILM Informatique * @see org.jopendocument.util.Unzip */ public class Zip { /** * Copie de from dans to seulement les entrées dont le nom n'est pas dans * <code>excludedEntries</code>. * * @param from le zip source. * @param excludedEntries les noms des entrées à exclure. * @param to le zip de destination, s'il existe déjà les entrées de from seront ajoutées aux * existantes. * @return le fichier zip dest. * @throws ZipException * @throws IOException */ static public Zip createFrom(File from, File to, Set excludedEntries) throws ZipException, IOException { Unzip unz = new Unzip(from); Zip res = new Zip(to); final Enumeration en = unz.entries(); while (en.hasMoreElements()) { ZipEntry entry = (ZipEntry) en.nextElement(); if (!excludedEntries.contains(entry.getName())) { res.zip(entry.getName(), unz.getInputStream(entry)); } } unz.close(); return res; } /** * Efface les entrées spécifées de src. Si dest existe, il sera ecrasé. * * @param src le zip source. * @param entriesName les noms des entrées à effacer. * @param dest le zip de destination. * @throws ZipException * @throws IOException */ static public void delete(File src, Set entriesName, File dest) throws ZipException, IOException { if (dest.exists()) dest.delete(); createFrom(src, dest, entriesName).close(); } // *** Instance private final OutputStream outstream; private ZipOutputStream zos; // is an entry open, ie addEntry() has been called but closeEntry() not yet private boolean entryOpen; /** * Construit un fichier zip. ATTN Le fichier passé sera écrasé lors de la première écriture. * * @param f le fichier dans lequel sauver, peut ne pas exister. * @throws FileNotFoundException if f cannot be written to. */ public Zip(File f) throws FileNotFoundException { this(new FileOutputStream(f)); } /** * Construit un fichier zip. * * @param out un stream dans lequel écrire. */ public Zip(OutputStream out) { this.outstream = out; this.zos = null; this.entryOpen = false; } public synchronized void close() throws IOException { if (this.zos != null) { // ferme aussi le FileOutputStream this.zos.close(); } } // *** Ecriture private synchronized ZipOutputStream getOutStream() { if (this.zos == null) { this.zos = new ZipOutputStream(this.outstream); } return this.zos; } /** * Ajoute newFile dans ce fichier. Il sera enregistré dans le zip directement à la racine. * * @param newFile le fichier à ajouter. * @throws IOException si le fichier ne peut etre zippé. */ public void zip(File newFile) throws IOException { // on ne garde que la derniere partie du chemin this.zip(newFile.getName(), new BufferedInputStream(new FileInputStream(newFile))); } /** * Zippe le contenu de <code>in</code>. * * @param name le nom de l'entrée. * @param in l'ajout. * @throws IOException si in ne peut etre zippé. */ public synchronized void zip(String name, InputStream in) throws IOException { this.putNextEntry(name); byte b[] = new byte[512]; int len = 0; while ((len = in.read(b)) != -1) { this.getOutStream().write(b, 0, len); } this.closeEntry(); } public void zip(String name, byte[] in, final boolean compressed) throws IOException { // don't make #zip(String, InputStream) call this method, it would require to read the // entire stream into memory if (compressed) this.zip(name, new ByteArrayInputStream(in)); else this.zipNonCompressed(name, in); } /** * Zip the passed array with the {@link ZipEntry#STORED} method. This method takes care of the * CRC and size. * * @param name the entry name. * @param in what to zip. * @throws IOException if an error occurs. */ public synchronized void zipNonCompressed(String name, byte[] in) throws IOException { final ZipEntry entry = new ZipEntry(name); entry.setMethod(ZipEntry.STORED); final CRC32 crc = new CRC32(); crc.update(in); entry.setCrc(crc.getValue()); entry.setSize(in.length); this.putNextEntry(entry); this.getOutStream().write(in); this.closeEntry(); } /** * Adds a new entry to this zip file. ATTN you must close the returned stream before you can add * to this zip again. * * @param name the name of the entry. * @return a stream to write to the entry. * @throws IOException if a pb occurs. */ public synchronized OutputStream createEntry(String name) throws IOException { this.putNextEntry(name); return new BufferedOutputStream(this.getOutStream()) { public void close() throws IOException { this.flush(); Zip.this.closeEntry(); } }; } private final synchronized void putNextEntry(String name) throws IOException, FileNotFoundException { this.putNextEntry(new ZipEntry(name)); } private final synchronized void putNextEntry(ZipEntry entry) throws IOException, FileNotFoundException { if (this.entryOpen) throw new IllegalStateException("previous entry not closed"); this.entryOpen = true; this.getOutStream().putNextEntry(entry); } protected final synchronized void closeEntry() throws IOException { this.getOutStream().closeEntry(); this.entryOpen = false; } }