OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
185 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
17 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils;
15
 
180 ilm 16
import org.openconcerto.utils.cc.ITransformerExn;
17
 
17 ilm 18
import java.io.BufferedInputStream;
19
import java.io.BufferedOutputStream;
20
import java.io.ByteArrayInputStream;
142 ilm 21
import java.io.ByteArrayOutputStream;
180 ilm 22
import java.io.Closeable;
17 ilm 23
import java.io.File;
24
import java.io.FileInputStream;
25
import java.io.FileNotFoundException;
26
import java.io.FileOutputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
142 ilm 30
import java.nio.ByteBuffer;
17 ilm 31
import java.util.Enumeration;
32
import java.util.Set;
180 ilm 33
import java.util.function.Function;
34
import java.util.jar.JarEntry;
35
import java.util.jar.JarOutputStream;
17 ilm 36
import java.util.zip.CRC32;
142 ilm 37
import java.util.zip.DeflaterOutputStream;
38
import java.util.zip.InflaterInputStream;
17 ilm 39
import java.util.zip.ZipEntry;
40
import java.util.zip.ZipException;
41
import java.util.zip.ZipOutputStream;
42
 
43
/**
44
 * Permet d'écrire dans un fichier zip.
45
 *
46
 * @author ILM Informatique
47
 * @see org.openconcerto.utils.Unzip
48
 */
180 ilm 49
public class Zip implements Closeable {
17 ilm 50
 
142 ilm 51
    static public byte[] deflate(final String s) throws IOException {
52
        return deflate(s.getBytes(StringUtils.UTF8));
53
    }
54
 
55
    static public byte[] deflate(final byte[] b) throws IOException {
56
        final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
57
        final DeflaterOutputStream out = new DeflaterOutputStream(bOut);
58
        out.write(b);
59
        out.close();
60
        return bOut.toByteArray();
61
    }
62
 
63
    static public ByteBuffer deflateToBuffer(final String s) throws IOException {
64
        return ByteBuffer.wrap(deflate(s));
65
    }
66
 
67
    static public String inflateToString(final ByteBuffer payload) throws IOException {
68
        return inflateToString(payload, payload.remaining());
69
    }
70
 
71
    static public String inflateToString(final ByteBuffer payload, final int length) throws IOException {
72
        final byte[] b = new byte[length];
73
        payload.get(b);
74
        return inflateToString(b);
75
    }
76
 
77
    static public String inflateToString(final byte[] b) throws IOException {
78
        return new String(inflate(b), StringUtils.UTF8);
79
    }
80
 
81
    static public byte[] inflate(final byte[] b) throws IOException {
82
        final InflaterInputStream in = new InflaterInputStream(new ByteArrayInputStream(b));
83
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
84
        StreamUtils.copy(in, out);
85
        out.close();
86
        return out.toByteArray();
87
    }
88
 
17 ilm 89
    /**
90
     * Copie de from dans to seulement les entrées dont le nom n'est pas dans
91
     * <code>excludedEntries</code>.
92
     *
93
     * @param from le zip source.
94
     * @param excludedEntries les noms des entrées à exclure.
95
     * @param to le zip de destination, s'il existe déjà les entrées de from seront ajoutées aux
96
     *        existantes.
97
     * @return le fichier zip dest.
98
     * @throws ZipException
99
     * @throws IOException
100
     */
142 ilm 101
    static public Zip createFrom(File from, File to, Set<String> excludedEntries) throws ZipException, IOException {
17 ilm 102
        Unzip unz = new Unzip(from);
103
        Zip res = new Zip(to);
142 ilm 104
        final Enumeration<? extends ZipEntry> en = unz.entries();
17 ilm 105
        while (en.hasMoreElements()) {
142 ilm 106
            final ZipEntry entry = en.nextElement();
17 ilm 107
            if (!excludedEntries.contains(entry.getName())) {
108
                res.zip(entry.getName(), unz.getInputStream(entry));
109
            }
110
        }
111
        unz.close();
112
        return res;
113
    }
114
 
115
    /**
116
     * Efface les entrées spécifées de src. Si dest existe, il sera ecrasé.
117
     *
118
     * @param src le zip source.
119
     * @param entriesName les noms des entrées à effacer.
120
     * @param dest le zip de destination.
121
     * @throws ZipException
122
     * @throws IOException
123
     */
142 ilm 124
    static public void delete(File src, Set<String> entriesName, File dest) throws ZipException, IOException {
17 ilm 125
        if (dest.exists())
126
            dest.delete();
127
        createFrom(src, dest, entriesName).close();
128
    }
129
 
180 ilm 130
    static public Zip createJar(final OutputStream out) {
131
        return new Zip(out, JarOutputStream::new, JarEntry::new);
132
    }
133
 
17 ilm 134
    // *** Instance
135
 
136
    private final OutputStream outstream;
180 ilm 137
    private final ITransformerExn<OutputStream, ZipOutputStream, IOException> createZipStream;
17 ilm 138
    private ZipOutputStream zos;
180 ilm 139
    private final Function<String, ZipEntry> createEntry;
17 ilm 140
    // is an entry open, ie addEntry() has been called but closeEntry() not yet
141
    private boolean entryOpen;
142
 
143
    /**
144
     * Construit un fichier zip. ATTN Le fichier passé sera écrasé lors de la première écriture.
145
     *
146
     * @param f le fichier dans lequel sauver, peut ne pas exister.
147
     * @throws FileNotFoundException if f cannot be written to.
148
     */
149
    public Zip(File f) throws FileNotFoundException {
150
        this(new FileOutputStream(f));
151
    }
152
 
153
    /**
154
     * Construit un fichier zip.
155
     *
156
     * @param out un stream dans lequel écrire.
157
     */
158
    public Zip(OutputStream out) {
180 ilm 159
        this(out, ZipOutputStream::new, ZipEntry::new);
160
    }
161
 
162
    public Zip(OutputStream out, final ITransformerExn<OutputStream, ZipOutputStream, IOException> createZipStream, final Function<String, ZipEntry> createEntry) {
17 ilm 163
        this.outstream = out;
180 ilm 164
        this.createZipStream = createZipStream;
17 ilm 165
        this.zos = null;
180 ilm 166
        this.createEntry = createEntry;
17 ilm 167
        this.entryOpen = false;
168
    }
169
 
180 ilm 170
    @Override
17 ilm 171
    public synchronized void close() throws IOException {
172
        if (this.zos != null) {
173
            // ferme aussi le FileOutputStream
174
            this.zos.close();
180 ilm 175
        } else {
176
            this.outstream.close();
17 ilm 177
        }
178
    }
179
 
180
    // *** Ecriture
181
 
180 ilm 182
    private synchronized ZipOutputStream getOutStream() throws IOException {
17 ilm 183
        if (this.zos == null) {
180 ilm 184
            this.zos = this.createZipStream.transformChecked(this.outstream);
17 ilm 185
        }
186
        return this.zos;
187
    }
188
 
180 ilm 189
    public ZipEntry createEntry(String name) {
190
        return this.createEntry.apply(name);
191
    }
192
 
17 ilm 193
    /**
194
     * Ajoute newFile dans ce fichier. Il sera enregistré dans le zip directement à la racine.
195
     *
196
     * @param newFile le fichier à ajouter.
197
     * @throws IOException si le fichier ne peut etre zippé.
198
     */
199
    public void zip(File newFile) throws IOException {
200
        // on ne garde que la derniere partie du chemin
180 ilm 201
        this.zip(newFile.getName(), newFile);
202
    }
203
 
204
    public void zip(final String entryName, final File newFile) throws IOException {
205
        final ZipEntry entry = this.createEntry(entryName);
206
        // TODO call ZipEntry.setCreationTime()
207
        entry.setTime(newFile.lastModified());
177 ilm 208
        try (final BufferedInputStream ins = new BufferedInputStream(new FileInputStream(newFile))) {
180 ilm 209
            this.zip(entry, ins);
177 ilm 210
        }
17 ilm 211
    }
212
 
213
    /**
214
     * Zippe le contenu de <code>in</code>.
215
     *
216
     * @param name le nom de l'entrée.
217
     * @param in l'ajout.
218
     * @throws IOException si in ne peut etre zippé.
219
     */
180 ilm 220
    public void zip(String name, InputStream in) throws IOException {
221
        this.zip(this.createEntry(name), in);
222
    }
17 ilm 223
 
180 ilm 224
    public synchronized void zip(final ZipEntry entry, InputStream in) throws IOException {
225
        this.putNextEntry(entry);
226
 
17 ilm 227
        byte b[] = new byte[512];
228
        int len = 0;
229
        while ((len = in.read(b)) != -1) {
230
            this.getOutStream().write(b, 0, len);
231
        }
232
 
233
        this.closeEntry();
234
    }
235
 
236
    public void zip(String name, byte[] in, final boolean compressed) throws IOException {
237
        // don't make #zip(String, InputStream) call this method, it would require to read the
238
        // entire stream into memory
239
        if (compressed)
240
            this.zip(name, new ByteArrayInputStream(in));
241
        else
242
            this.zipNonCompressed(name, in);
243
    }
244
 
245
    /**
246
     * Zip the passed array with the {@link ZipEntry#STORED} method. This method takes care of the
247
     * CRC and size.
248
     *
249
     * @param name the entry name.
250
     * @param in what to zip.
251
     * @throws IOException if an error occurs.
252
     */
253
    public synchronized void zipNonCompressed(String name, byte[] in) throws IOException {
180 ilm 254
        final ZipEntry entry = createEntry(name);
17 ilm 255
        entry.setMethod(ZipEntry.STORED);
256
        final CRC32 crc = new CRC32();
257
        crc.update(in);
258
        entry.setCrc(crc.getValue());
259
        entry.setSize(in.length);
260
 
261
        this.putNextEntry(entry);
262
        this.getOutStream().write(in);
263
        this.closeEntry();
264
    }
265
 
266
    /**
267
     * Adds a new entry to this zip file. ATTN you must close the returned stream before you can add
268
     * to this zip again.
269
     *
270
     * @param name the name of the entry.
271
     * @return a stream to write to the entry.
272
     * @throws IOException if a pb occurs.
273
     */
180 ilm 274
    public synchronized OutputStream createEntryStream(String name) throws IOException {
17 ilm 275
        this.putNextEntry(name);
276
        return new BufferedOutputStream(this.getOutStream()) {
277
            public void close() throws IOException {
278
                this.flush();
279
                Zip.this.closeEntry();
280
            }
281
        };
282
    }
283
 
284
    private final synchronized void putNextEntry(String name) throws IOException, FileNotFoundException {
180 ilm 285
        this.putNextEntry(createEntry(name));
17 ilm 286
    }
287
 
288
    private final synchronized void putNextEntry(ZipEntry entry) throws IOException, FileNotFoundException {
289
        if (this.entryOpen)
290
            throw new IllegalStateException("previous entry not closed");
291
        this.entryOpen = true;
292
        this.getOutStream().putNextEntry(entry);
293
    }
294
 
295
    protected final synchronized void closeEntry() throws IOException {
296
        this.getOutStream().closeEntry();
297
        this.entryOpen = false;
298
    }
299
 
300
}