1 package org.djutils.io;
2
3 import java.io.BufferedWriter;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.FileWriter;
8 import java.io.IOException;
9 import java.io.OutputStreamWriter;
10 import java.util.zip.ZipEntry;
11 import java.util.zip.ZipOutputStream;
12
13 /**
14 * File writer for multiple files in to a zip file. Typical use is:
15 *
16 * <pre>
17 * try (CompressedFileWriter compressedFileWriter = new CompressedFileWriter("CsvData.zip"))
18 * {
19 * BufferedWriter bufferedWriter = compressedFileWriter.next("data_2023.csv");
20 *
21 * // write data for data_2023
22 * bufferedWriter.write(...);
23 *
24 * compressedFileWriter.next("data_2024.csv");
25 *
26 * // write data for data_2024
27 * bufferedWriter.write(...);
28 * }
29 * </pre>
30 *
31 * If the {@code BufferedWriter} is closed, so too is the {@code CompressedFileWriter}. Any consumers of the
32 * {@code BufferedWriter} should thus not close it.
33 * <p>
34 * Copyright (c) 2013-2025 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
35 * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
36 * </p>
37 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
38 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
39 * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
40 */
41 public final class CompressedFileWriter implements AutoCloseable
42 {
43
44 /** Zip output stream to create new zip entries. */
45 private final ZipOutputStream zipOutputStream;
46
47 /** Buffered writer to write in to. */
48 private BufferedWriter bufferedWriter;
49
50 /**
51 * Constructor.
52 * @param file file, if this does not end with .zip (case insensitive), ".zip" will be appended to it
53 * @throws FileNotFoundException if the zip file can not be written
54 */
55 public CompressedFileWriter(final String file) throws FileNotFoundException
56 {
57 this.zipOutputStream =
58 new ZipOutputStream(new FileOutputStream(file.toLowerCase().endsWith(".zip") ? file : file + ".zip"));
59 this.bufferedWriter = new BufferedWriter(new OutputStreamWriter(this.zipOutputStream));
60 }
61
62 /**
63 * Closes the previous file in the zip file, and opens up the next file. The {@code BufferedWriter} returned is the same for
64 * each call on a {@code CompressedFileWriter}.
65 * @param name name of the next file in the zip file
66 * @return writer to write the next file in to.
67 * @throws IOException if no next entry could be created in the zip file
68 */
69 public BufferedWriter next(final String name) throws IOException
70 {
71 this.bufferedWriter.flush();
72 this.zipOutputStream.putNextEntry(new ZipEntry(name));
73 return this.bufferedWriter;
74 }
75
76 @Override
77 public void close() throws IOException
78 {
79 this.bufferedWriter.flush();
80 this.zipOutputStream.close();
81 }
82
83 /**
84 * Creates a writer to write data to a file, which can be a zipped file or a regular file. In particular if
85 * {@code zipped = true}, then with {@code file = "myFile.csv"}, a file {@code myFile.csv.zip} will be created in which a
86 * file {@code myFile.csv} is located. Writing occurs on this file.
87 * @param filePath path of the file to write; in case of a zipped file, the filename of the zip-file will end with
88 * .zip, and the filename in the zip file will be the the filename without .zip.
89 * @param zipped whether to contain the file in a zip file
90 * @return BufferedWriter writer tot write in to
91 * @throws IOException on error with filenames, file writing, closing, etc.
92 */
93 public static BufferedWriter create(final String filePath, final boolean zipped) throws IOException
94 {
95 if (zipped)
96 {
97 ZipOutputStream zipOutputStream = new ZipOutputStream(
98 new FileOutputStream(filePath.toLowerCase().endsWith(".zip") ? filePath : filePath + ".zip"));
99 String fileName = new File(filePath).getName();
100 fileName = fileName.toLowerCase().endsWith(".zip") ? fileName.substring(0, fileName.length() - 4) : fileName;
101 zipOutputStream.putNextEntry(new ZipEntry(fileName));
102 return new BufferedWriter(new OutputStreamWriter(zipOutputStream));
103 }
104 return new BufferedWriter(new FileWriter(filePath));
105 }
106
107 }