change package for Streams to nikiroo.utils.streams
[nikiroo-utils.git] / src / be / nikiroo / utils / IOUtils.java
CommitLineData
ec1f3444
NR
1package be.nikiroo.utils;
2
80500544 3import java.io.ByteArrayOutputStream;
ec1f3444
NR
4import java.io.File;
5import java.io.FileInputStream;
6import java.io.FileOutputStream;
ec1f3444
NR
7import java.io.IOException;
8import java.io.InputStream;
9import java.io.OutputStream;
59864f77
NR
10import java.util.ArrayList;
11import java.util.List;
ec1f3444 12import java.util.zip.ZipEntry;
4110f63b 13import java.util.zip.ZipInputStream;
ec1f3444
NR
14import java.util.zip.ZipOutputStream;
15
8e76f6ab
NR
16import be.nikiroo.utils.streams.MarkableFileInputStream;
17
ec1f3444 18/**
bb60bd13 19 * This class offer some utilities based around Streams and Files.
ec1f3444
NR
20 *
21 * @author niki
22 */
23public class IOUtils {
24 /**
25 * Write the data to the given {@link File}.
26 *
27 * @param in
28 * the data source
29 * @param target
30 * the target {@link File}
31 *
32 * @throws IOException
33 * in case of I/O error
34 */
35 public static void write(InputStream in, File target) throws IOException {
36 OutputStream out = new FileOutputStream(target);
37 try {
38 write(in, out);
39 } finally {
40 out.close();
41 }
42 }
43
44 /**
45 * Write the data to the given {@link OutputStream}.
46 *
47 * @param in
48 * the data source
db31c358 49 * @param out
ec1f3444
NR
50 * the target {@link OutputStream}
51 *
52 * @throws IOException
53 * in case of I/O error
54 */
55 public static void write(InputStream in, OutputStream out)
56 throws IOException {
ea152609 57 byte buffer[] = new byte[4096];
e378894c
NR
58 int len = in.read(buffer);
59 while (len > 0) {
ec1f3444 60 out.write(buffer, 0, len);
e378894c 61 len = in.read(buffer);
ec1f3444
NR
62 }
63 }
64
65 /**
66 * Recursively Add a {@link File} (which can thus be a directory, too) to a
67 * {@link ZipOutputStream}.
68 *
69 * @param zip
70 * the stream
71 * @param base
72 * the path to prepend to the ZIP info before the actual
73 * {@link File} path
74 * @param target
75 * the source {@link File} (which can be a directory)
76 * @param targetIsRoot
77 * FALSE if we need to add a {@link ZipEntry} for base/target,
78 * TRUE to add it at the root of the ZIP
79 *
80 * @throws IOException
81 * in case of I/O error
82 */
83 public static void zip(ZipOutputStream zip, String base, File target,
84 boolean targetIsRoot) throws IOException {
85 if (target.isDirectory()) {
86 if (!targetIsRoot) {
87 if (base == null || base.isEmpty()) {
88 base = target.getName();
89 } else {
90 base += "/" + target.getName();
91 }
92 zip.putNextEntry(new ZipEntry(base + "/"));
93 }
0988831f
NR
94
95 File[] files = target.listFiles();
96 if (files != null) {
97 for (File file : files) {
98 zip(zip, base, file, false);
99 }
ec1f3444
NR
100 }
101 } else {
102 if (base == null || base.isEmpty()) {
103 base = target.getName();
104 } else {
105 base += "/" + target.getName();
106 }
107 zip.putNextEntry(new ZipEntry(base));
108 FileInputStream in = new FileInputStream(target);
109 try {
110 IOUtils.write(in, zip);
111 } finally {
112 in.close();
113 }
114 }
115 }
116
117 /**
118 * Zip the given source into dest.
119 *
120 * @param src
121 * the source {@link File} (which can be a directory)
122 * @param dest
123 * the destination <tt>.zip</tt> file
db31c358 124 * @param srcIsRoot
ec1f3444
NR
125 * FALSE if we need to add a {@link ZipEntry} for src, TRUE to
126 * add it at the root of the ZIP
127 *
128 * @throws IOException
129 * in case of I/O error
130 */
131 public static void zip(File src, File dest, boolean srcIsRoot)
132 throws IOException {
133 OutputStream out = new FileOutputStream(dest);
134 try {
135 ZipOutputStream zip = new ZipOutputStream(out);
136 try {
137 IOUtils.zip(zip, "", src, srcIsRoot);
138 } finally {
139 zip.close();
140 }
141 } finally {
142 out.close();
143 }
144 }
145
4110f63b
NR
146 /**
147 * Unzip the given ZIP file into the target directory.
148 *
149 * @param zipFile
150 * the ZIP file
151 * @param targetDirectory
152 * the target directory
153 *
154 * @return the number of extracted files (not directories)
155 *
156 * @throws IOException
157 * in case of I/O errors
158 */
159 public static long unzip(File zipFile, File targetDirectory)
160 throws IOException {
161 long count = 0;
162
163 if (targetDirectory.exists() && targetDirectory.isFile()) {
164 throw new IOException("Cannot unzip " + zipFile + " into "
165 + targetDirectory + ": it is not a directory");
166 }
167
168 targetDirectory.mkdir();
169 if (!targetDirectory.exists()) {
170 throw new IOException("Cannot create target directory "
171 + targetDirectory);
172 }
173
174 FileInputStream in = new FileInputStream(zipFile);
175 try {
176 ZipInputStream zipStream = new ZipInputStream(in);
177 try {
178 for (ZipEntry entry = zipStream.getNextEntry(); entry != null; entry = zipStream
179 .getNextEntry()) {
180 File file = new File(targetDirectory, entry.getName());
181 if (entry.isDirectory()) {
182 file.mkdirs();
183 } else {
184 IOUtils.write(zipStream, file);
185 count++;
186 }
187 }
188 } finally {
189 zipStream.close();
190 }
191 } finally {
192 in.close();
193 }
194
195 return count;
196 }
197
ec1f3444
NR
198 /**
199 * Write the {@link String} content to {@link File}.
200 *
201 * @param dir
202 * the directory where to write the {@link File}
203 * @param filename
204 * the {@link File} name
205 * @param content
206 * the content
207 *
208 * @throws IOException
209 * in case of I/O error
210 */
211 public static void writeSmallFile(File dir, String filename, String content)
212 throws IOException {
213 if (!dir.exists()) {
214 dir.mkdirs();
215 }
216
ce060f5a
NR
217 writeSmallFile(new File(dir, filename), content);
218 }
219
220 /**
221 * Write the {@link String} content to {@link File}.
222 *
223 * @param file
224 * the {@link File} to write
225 * @param content
226 * the content
227 *
228 * @throws IOException
229 * in case of I/O error
230 */
231 public static void writeSmallFile(File file, String content)
232 throws IOException {
e378894c 233 FileOutputStream out = new FileOutputStream(file);
ec1f3444 234 try {
e378894c 235 out.write(content.getBytes("UTF-8"));
ec1f3444 236 } finally {
e378894c 237 out.close();
ec1f3444
NR
238 }
239 }
240
241 /**
242 * Read the whole {@link File} content into a {@link String}.
243 *
244 * @param file
245 * the {@link File}
246 *
247 * @return the content
248 *
249 * @throws IOException
250 * in case of I/O error
251 */
252 public static String readSmallFile(File file) throws IOException {
3f8349b7
NR
253 InputStream stream = new FileInputStream(file);
254 try {
255 return readSmallStream(stream);
256 } finally {
257 stream.close();
258 }
259 }
260
261 /**
262 * Read the whole {@link InputStream} content into a {@link String}.
263 *
264 * @param stream
265 * the {@link InputStream}
266 *
267 * @return the content
268 *
269 * @throws IOException
270 * in case of I/O error
271 */
272 public static String readSmallStream(InputStream stream) throws IOException {
e378894c
NR
273 ByteArrayOutputStream out = new ByteArrayOutputStream();
274 try {
275 write(stream, out);
276 return out.toString("UTF-8");
277 } finally {
278 // do NOT close, or the related stream will be closed, too
279 // out.close();
ec1f3444
NR
280 }
281 }
282
59864f77
NR
283 /**
284 * Recursively delete the given {@link File}, which may of course also be a
285 * directory.
286 * <p>
287 * Will either silently continue or throw an exception in case of error,
288 * depending upon the parameters.
289 *
290 * @param target
291 * the target to delete
292 * @param exception
293 * TRUE to throw an {@link IOException} in case of error, FALSE
294 * to silently continue
295 *
296 * @return TRUE if all files were deleted, FALSE if an error occurred
297 *
298 * @throws IOException
299 * if an error occurred and the parameters allow an exception to
300 * be thrown
301 */
302 public static boolean deltree(File target, boolean exception)
303 throws IOException {
304 List<File> list = deltree(target, null);
305 if (exception && !list.isEmpty()) {
0988831f 306 StringBuilder slist = new StringBuilder();
59864f77 307 for (File file : list) {
0988831f 308 slist.append("\n").append(file.getPath());
59864f77
NR
309 }
310
311 throw new IOException("Cannot delete all the files from: <" //
0988831f 312 + target + ">:" + slist.toString());
59864f77
NR
313 }
314
315 return list.isEmpty();
316 }
317
ec1f3444
NR
318 /**
319 * Recursively delete the given {@link File}, which may of course also be a
320 * directory.
321 * <p>
322 * Will silently continue in case of error.
323 *
324 * @param target
325 * the target to delete
59864f77
NR
326 *
327 * @return TRUE if all files were deleted, FALSE if an error occurred
328 */
329 public static boolean deltree(File target) {
330 return deltree(target, null).isEmpty();
331 }
332
333 /**
334 * Recursively delete the given {@link File}, which may of course also be a
335 * directory.
336 * <p>
337 * Will collect all {@link File} that cannot be deleted in the given
338 * accumulator.
339 *
340 * @param target
341 * the target to delete
342 * @param errorAcc
343 * the accumulator to use for errors, or NULL to create a new one
344 *
345 * @return the errors accumulator
ec1f3444 346 */
59864f77
NR
347 public static List<File> deltree(File target, List<File> errorAcc) {
348 if (errorAcc == null) {
349 errorAcc = new ArrayList<File>();
350 }
351
7ee9568b
NR
352 File[] files = target.listFiles();
353 if (files != null) {
354 for (File file : files) {
59864f77 355 errorAcc = deltree(file, errorAcc);
ec1f3444
NR
356 }
357 }
358
359 if (!target.delete()) {
59864f77 360 errorAcc.add(target);
ec1f3444 361 }
59864f77
NR
362
363 return errorAcc;
ec1f3444 364 }
b607df60 365
16d59378
NR
366 /**
367 * Open the given /-separated resource (from the binary root).
368 *
369 * @param name
370 * the resource name
371 *
372 * @return the opened resource if found, NLL if not
373 */
374 public static InputStream openResource(String name) {
375 ClassLoader loader = IOUtils.class.getClassLoader();
376 if (loader == null) {
377 loader = ClassLoader.getSystemClassLoader();
378 }
379
380 return loader.getResourceAsStream(name);
381 }
80500544
NR
382
383 /**
384 * Return a resetable {@link InputStream} from this stream, and reset it.
385 *
386 * @param in
387 * the input stream
388 * @return the resetable stream, which <b>may</b> be the same
389 *
390 * @throws IOException
391 * in case of I/O error
392 */
393 public static InputStream forceResetableStream(InputStream in)
394 throws IOException {
80500544
NR
395 boolean resetable = in.markSupported();
396 if (resetable) {
397 try {
398 in.reset();
399 } catch (IOException e) {
400 resetable = false;
401 }
402 }
403
404 if (resetable) {
405 return in;
406 }
407
75712fb3 408 final File tmp = File.createTempFile(".tmp-stream.", ".tmp");
80500544
NR
409 try {
410 write(in, tmp);
75712fb3
NR
411 in.close();
412
413 final FileInputStream fis = new FileInputStream(tmp);
414 return new MarkableFileInputStream(fis) {
415 @Override
416 public void close() throws IOException {
417 try {
418 try {
419 super.close();
420 } finally {
421 try {
422 fis.close();
423 } catch (IOException e) {
424 }
425 }
426 } finally {
427 tmp.delete();
428 }
80500544 429 }
75712fb3
NR
430 };
431 } catch (IOException e) {
432 tmp.delete();
433 throw e;
80500544
NR
434 }
435 }
436
437 /**
438 * Convert the {@link InputStream} into a byte array.
439 *
440 * @param in
441 * the input stream
442 *
443 * @return the array
444 *
445 * @throws IOException
446 * in case of I/O error
447 */
448 public static byte[] toByteArray(InputStream in) throws IOException {
449 ByteArrayOutputStream out = new ByteArrayOutputStream();
e378894c
NR
450 try {
451 write(in, out);
452 return out.toByteArray();
453 } finally {
454 out.close();
455 }
80500544 456 }
bb60bd13
NR
457
458 /**
459 * Convert the {@link File} into a byte array.
460 *
461 * @param file
462 * the input {@link File}
463 *
464 * @return the array
465 *
466 * @throws IOException
467 * in case of I/O error
468 */
469 public static byte[] toByteArray(File file) throws IOException {
470 FileInputStream fis = new FileInputStream(file);
471 try {
472 return toByteArray(fis);
473 } finally {
474 fis.close();
475 }
476 }
ec1f3444 477}