001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.utils; 020 021import java.io.ByteArrayOutputStream; 022import java.io.Closeable; 023import java.io.EOFException; 024import java.io.File; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.OutputStream; 028import java.nio.ByteBuffer; 029import java.nio.channels.ReadableByteChannel; 030import java.nio.file.Files; 031import java.nio.file.LinkOption; 032 033/** 034 * Utility functions 035 * @Immutable (has mutable data but it is write-only) 036 */ 037public final class IOUtils { 038 039 private static final int COPY_BUF_SIZE = 8024; 040 private static final int SKIP_BUF_SIZE = 4096; 041 042 /** 043 * Empty array of of type {@link LinkOption}. 044 * 045 * @since 1.21 046 */ 047 public static final LinkOption[] EMPTY_LINK_OPTIONS = {}; 048 049 // This buffer does not need to be synchronized because it is write only; the contents are ignored 050 // Does not affect Immutability 051 private static final byte[] SKIP_BUF = new byte[SKIP_BUF_SIZE]; 052 053 /** Private constructor to prevent instantiation of this utility class. */ 054 private IOUtils(){ 055 } 056 057 /** 058 * Copies the content of a InputStream into an OutputStream. 059 * Uses a default buffer size of 8024 bytes. 060 * 061 * @param input 062 * the InputStream to copy 063 * @param output 064 * the target Stream 065 * @return the number of bytes copied 066 * @throws IOException 067 * if an error occurs 068 */ 069 public static long copy(final InputStream input, final OutputStream output) throws IOException { 070 return copy(input, output, COPY_BUF_SIZE); 071 } 072 073 /** 074 * Copies the content of a InputStream into an OutputStream 075 * 076 * @param input 077 * the InputStream to copy 078 * @param output 079 * the target Stream 080 * @param buffersize 081 * the buffer size to use, must be bigger than 0 082 * @return the number of bytes copied 083 * @throws IOException 084 * if an error occurs 085 * @throws IllegalArgumentException 086 * if buffersize is smaller than or equal to 0 087 */ 088 public static long copy(final InputStream input, final OutputStream output, final int buffersize) throws IOException { 089 if (buffersize < 1) { 090 throw new IllegalArgumentException("buffersize must be bigger than 0"); 091 } 092 final byte[] buffer = new byte[buffersize]; 093 int n = 0; 094 long count=0; 095 while (-1 != (n = input.read(buffer))) { 096 output.write(buffer, 0, n); 097 count += n; 098 } 099 return count; 100 } 101 102 /** 103 * Skips the given number of bytes by repeatedly invoking skip on 104 * the given input stream if necessary. 105 * 106 * <p>In a case where the stream's skip() method returns 0 before 107 * the requested number of bytes has been skip this implementation 108 * will fall back to using the read() method.</p> 109 * 110 * <p>This method will only skip less than the requested number of 111 * bytes if the end of the input stream has been reached.</p> 112 * 113 * @param input stream to skip bytes in 114 * @param numToSkip the number of bytes to skip 115 * @return the number of bytes actually skipped 116 * @throws IOException on error 117 */ 118 public static long skip(final InputStream input, long numToSkip) throws IOException { 119 final long available = numToSkip; 120 while (numToSkip > 0) { 121 final long skipped = input.skip(numToSkip); 122 if (skipped == 0) { 123 break; 124 } 125 numToSkip -= skipped; 126 } 127 128 while (numToSkip > 0) { 129 final int read = readFully(input, SKIP_BUF, 0, 130 (int) Math.min(numToSkip, SKIP_BUF_SIZE)); 131 if (read < 1) { 132 break; 133 } 134 numToSkip -= read; 135 } 136 return available - numToSkip; 137 } 138 139 /** 140 * Reads as much from the file as possible to fill the given array. 141 * 142 * <p>This method may invoke read repeatedly to fill the array and 143 * only read less bytes than the length of the array if the end of 144 * the stream has been reached.</p> 145 * 146 * @param file file to read 147 * @param array buffer to fill 148 * @return the number of bytes actually read 149 * @throws IOException on error 150 * @since 1.20 151 */ 152 public static int read(final File file, final byte[] array) throws IOException { 153 try (InputStream inputStream = Files.newInputStream(file.toPath())) { 154 return readFully(inputStream, array, 0, array.length); 155 } 156 } 157 158 /** 159 * Reads as much from input as possible to fill the given array. 160 * 161 * <p>This method may invoke read repeatedly to fill the array and 162 * only read less bytes than the length of the array if the end of 163 * the stream has been reached.</p> 164 * 165 * @param input stream to read from 166 * @param array buffer to fill 167 * @return the number of bytes actually read 168 * @throws IOException on error 169 */ 170 public static int readFully(final InputStream input, final byte[] array) throws IOException { 171 return readFully(input, array, 0, array.length); 172 } 173 174 /** 175 * Reads as much from input as possible to fill the given array 176 * with the given amount of bytes. 177 * 178 * <p>This method may invoke read repeatedly to read the bytes and 179 * only read less bytes than the requested length if the end of 180 * the stream has been reached.</p> 181 * 182 * @param input stream to read from 183 * @param array buffer to fill 184 * @param offset offset into the buffer to start filling at 185 * @param len of bytes to read 186 * @return the number of bytes actually read 187 * @throws IOException 188 * if an I/O error has occurred 189 */ 190 public static int readFully(final InputStream input, final byte[] array, final int offset, final int len) 191 throws IOException { 192 if (len < 0 || offset < 0 || len + offset > array.length || len + offset < 0) { 193 throw new IndexOutOfBoundsException(); 194 } 195 int count = 0, x = 0; 196 while (count != len) { 197 x = input.read(array, offset + count, len - count); 198 if (x == -1) { 199 break; 200 } 201 count += x; 202 } 203 return count; 204 } 205 206 /** 207 * Reads {@code b.remaining()} bytes from the given channel 208 * starting at the current channel's position. 209 * 210 * <p>This method reads repeatedly from the channel until the 211 * requested number of bytes are read. This method blocks until 212 * the requested number of bytes are read, the end of the channel 213 * is detected, or an exception is thrown.</p> 214 * 215 * @param channel the channel to read from 216 * @param b the buffer into which the data is read. 217 * @throws IOException - if an I/O error occurs. 218 * @throws EOFException - if the channel reaches the end before reading all the bytes. 219 */ 220 public static void readFully(final ReadableByteChannel channel, final ByteBuffer b) throws IOException { 221 final int expectedLength = b.remaining(); 222 int read = 0; 223 while (read < expectedLength) { 224 final int readNow = channel.read(b); 225 if (readNow <= 0) { 226 break; 227 } 228 read += readNow; 229 } 230 if (read < expectedLength) { 231 throw new EOFException(); 232 } 233 } 234 235 // toByteArray(InputStream) copied from: 236 // commons/proper/io/trunk/src/main/java/org/apache/commons/io/IOUtils.java?revision=1428941 237 // January 8th, 2013 238 // 239 // Assuming our copy() works just as well as theirs! :-) 240 241 /** 242 * Gets the contents of an <code>InputStream</code> as a <code>byte[]</code>. 243 * <p> 244 * This method buffers the input internally, so there is no need to use a 245 * <code>BufferedInputStream</code>. 246 * 247 * @param input the <code>InputStream</code> to read from 248 * @return the requested byte array 249 * @throws NullPointerException if the input is null 250 * @throws IOException if an I/O error occurs 251 * @since 1.5 252 */ 253 public static byte[] toByteArray(final InputStream input) throws IOException { 254 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 255 copy(input, output); 256 return output.toByteArray(); 257 } 258 259 /** 260 * Closes the given Closeable and swallows any IOException that may occur. 261 * @param c Closeable to close, can be null 262 * @since 1.7 263 */ 264 public static void closeQuietly(final Closeable c) { 265 if (c != null) { 266 try { 267 c.close(); 268 } catch (final IOException ignored) { // NOPMD NOSONAR 269 } 270 } 271 } 272 273 /** 274 * Copies the source file to the given output stream. 275 * @param sourceFile The file to read. 276 * @param outputStream The output stream to write. 277 * @throws IOException if an I/O error occurs when reading or writing. 278 * @since 1.21 279 */ 280 public static void copy(final File sourceFile, final OutputStream outputStream) throws IOException { 281 Files.copy(sourceFile.toPath(), outputStream); 282 } 283 284 /** 285 * Copies part of the content of a InputStream into an OutputStream. 286 * Uses a default buffer size of 8024 bytes. 287 * 288 * @param input 289 * the InputStream to copy 290 * @param output 291 * the target Stream 292 * @param len 293 * maximum amount of bytes to copy 294 * @return the number of bytes copied 295 * @throws IOException 296 * if an error occurs 297 * @since 1.21 298 */ 299 public static long copyRange(final InputStream input, final long len, final OutputStream output) 300 throws IOException { 301 return copyRange(input, len, output, COPY_BUF_SIZE); 302 } 303 304 /** 305 * Copies part of the content of a InputStream into an OutputStream 306 * 307 * @param input 308 * the InputStream to copy 309 * @param len 310 * maximum amount of bytes to copy 311 * @param output 312 * the target Stream 313 * @param buffersize 314 * the buffer size to use, must be bigger than 0 315 * @return the number of bytes copied 316 * @throws IOException 317 * if an error occurs 318 * @throws IllegalArgumentException 319 * if buffersize is smaller than or equal to 0 320 * @since 1.21 321 */ 322 public static long copyRange(final InputStream input, final long len, final OutputStream output, 323 final int buffersize) throws IOException { 324 if (buffersize < 1) { 325 throw new IllegalArgumentException("buffersize must be bigger than 0"); 326 } 327 final byte[] buffer = new byte[(int) Math.min(buffersize, len)]; 328 int n = 0; 329 long count = 0; 330 while (count < len && -1 != (n = input.read(buffer, 0, (int) Math.min(len - count, buffer.length)))) { 331 output.write(buffer, 0, n); 332 count += n; 333 } 334 return count; 335 } 336 337 /** 338 * Gets part of the contents of an <code>InputStream</code> as a <code>byte[]</code>. 339 * 340 * @param input the <code>InputStream</code> to read from 341 * @param len 342 * maximum amount of bytes to copy 343 * @return the requested byte array 344 * @throws NullPointerException if the input is null 345 * @throws IOException if an I/O error occurs 346 * @since 1.21 347 */ 348 public static byte[] readRange(final InputStream input, final int len) throws IOException { 349 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 350 copyRange(input, len, output); 351 return output.toByteArray(); 352 } 353 354 /** 355 * Gets part of the contents of an <code>ReadableByteChannel</code> as a <code>byte[]</code>. 356 * 357 * @param input the <code>ReadableByteChannel</code> to read from 358 * @param len 359 * maximum amount of bytes to copy 360 * @return the requested byte array 361 * @throws NullPointerException if the input is null 362 * @throws IOException if an I/O error occurs 363 * @since 1.21 364 */ 365 public static byte[] readRange(final ReadableByteChannel input, final int len) throws IOException { 366 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 367 final ByteBuffer b = ByteBuffer.allocate(Math.min(len, COPY_BUF_SIZE)); 368 int read = 0; 369 while (read < len) { 370 final int readNow = input.read(b); 371 if (readNow <= 0) { 372 break; 373 } 374 output.write(b.array(), 0, readNow); 375 b.rewind(); 376 read += readNow; 377 } 378 return output.toByteArray(); 379 } 380 381}