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 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 /** 054 * Closes the given Closeable and swallows any IOException that may occur. 055 * @param c Closeable to close, can be null 056 * @since 1.7 057 */ 058 public static void closeQuietly(final Closeable c) { 059 if (c != null) { 060 try { 061 c.close(); 062 } catch (final IOException ignored) { // NOPMD NOSONAR 063 } 064 } 065 } 066 067 /** 068 * Copies the source file to the given output stream. 069 * @param sourceFile The file to read. 070 * @param outputStream The output stream to write. 071 * @throws IOException if an I/O error occurs when reading or writing. 072 * @since 1.21 073 */ 074 public static void copy(final File sourceFile, final OutputStream outputStream) throws IOException { 075 Files.copy(sourceFile.toPath(), outputStream); 076 } 077 078 /** 079 * Copies the content of a InputStream into an OutputStream. 080 * Uses a default buffer size of 8024 bytes. 081 * 082 * @param input 083 * the InputStream to copy 084 * @param output 085 * the target, may be null to simulate output to dev/null on Linux and NUL on Windows 086 * @return the number of bytes copied 087 * @throws IOException 088 * if an error occurs 089 */ 090 public static long copy(final InputStream input, final OutputStream output) throws IOException { 091 return copy(input, output, COPY_BUF_SIZE); 092 } 093 094 /** 095 * Copies the content of a InputStream into an OutputStream 096 * 097 * @param input 098 * the InputStream to copy 099 * @param output 100 * the target, may be null to simulate output to dev/null on Linux and NUL on Windows 101 * @param bufferSize 102 * the buffer size to use, must be bigger than 0 103 * @return the number of bytes copied 104 * @throws IOException 105 * if an error occurs 106 * @throws IllegalArgumentException 107 * if bufferSize is smaller than or equal to 0 108 */ 109 public static long copy(final InputStream input, final OutputStream output, final int bufferSize) throws IOException { 110 if (bufferSize < 1) { 111 throw new IllegalArgumentException("bufferSize must be bigger than 0"); 112 } 113 final byte[] buffer = new byte[bufferSize]; 114 int n = 0; 115 long count = 0; 116 while (-1 != (n = input.read(buffer))) { 117 if (output != null) { 118 output.write(buffer, 0, n); 119 } 120 count += n; 121 } 122 return count; 123 } 124 125 /** 126 * Copies part of the content of a InputStream into an OutputStream. 127 * Uses a default buffer size of 8024 bytes. 128 * 129 * @param input 130 * the InputStream to copy 131 * @param output 132 * the target Stream 133 * @param len 134 * maximum amount of bytes to copy 135 * @return the number of bytes copied 136 * @throws IOException 137 * if an error occurs 138 * @since 1.21 139 */ 140 public static long copyRange(final InputStream input, final long len, final OutputStream output) 141 throws IOException { 142 return copyRange(input, len, output, COPY_BUF_SIZE); 143 } 144 145 /** 146 * Copies part of the content of a InputStream into an OutputStream 147 * 148 * @param input 149 * the InputStream to copy 150 * @param len 151 * maximum amount of bytes to copy 152 * @param output 153 * the target, may be null to simulate output to dev/null on Linux and NUL on Windows 154 * @param bufferSize 155 * the buffer size to use, must be bigger than 0 156 * @return the number of bytes copied 157 * @throws IOException 158 * if an error occurs 159 * @throws IllegalArgumentException 160 * if bufferSize is smaller than or equal to 0 161 * @since 1.21 162 */ 163 public static long copyRange(final InputStream input, final long len, final OutputStream output, 164 final int bufferSize) throws IOException { 165 if (bufferSize < 1) { 166 throw new IllegalArgumentException("bufferSize must be bigger than 0"); 167 } 168 final byte[] buffer = new byte[(int) Math.min(bufferSize, len)]; 169 int n = 0; 170 long count = 0; 171 while (count < len && -1 != (n = input.read(buffer, 0, (int) Math.min(len - count, buffer.length)))) { 172 if (output != null) { 173 output.write(buffer, 0, n); 174 } 175 count += n; 176 } 177 return count; 178 } 179 180 /** 181 * Reads as much from the file as possible to fill the given array. 182 * 183 * <p>This method may invoke read repeatedly to fill the array and 184 * only read less bytes than the length of the array if the end of 185 * the stream has been reached.</p> 186 * 187 * @param file file to read 188 * @param array buffer to fill 189 * @return the number of bytes actually read 190 * @throws IOException on error 191 * @since 1.20 192 */ 193 public static int read(final File file, final byte[] array) throws IOException { 194 try (InputStream inputStream = Files.newInputStream(file.toPath())) { 195 return readFully(inputStream, array, 0, array.length); 196 } 197 } 198 199 /** 200 * Reads as much from input as possible to fill the given array. 201 * 202 * <p>This method may invoke read repeatedly to fill the array and 203 * only read less bytes than the length of the array if the end of 204 * the stream has been reached.</p> 205 * 206 * @param input stream to read from 207 * @param array buffer to fill 208 * @return the number of bytes actually read 209 * @throws IOException on error 210 */ 211 public static int readFully(final InputStream input, final byte[] array) throws IOException { 212 return readFully(input, array, 0, array.length); 213 } 214 215 // toByteArray(InputStream) copied from: 216 // commons/proper/io/trunk/src/main/java/org/apache/commons/io/IOUtils.java?revision=1428941 217 // January 8th, 2013 218 // 219 // Assuming our copy() works just as well as theirs! :-) 220 221 /** 222 * Reads as much from input as possible to fill the given array 223 * with the given amount of bytes. 224 * 225 * <p>This method may invoke read repeatedly to read the bytes and 226 * only read less bytes than the requested length if the end of 227 * the stream has been reached.</p> 228 * 229 * @param input stream to read from 230 * @param array buffer to fill 231 * @param offset offset into the buffer to start filling at 232 * @param len of bytes to read 233 * @return the number of bytes actually read 234 * @throws IOException 235 * if an I/O error has occurred 236 */ 237 public static int readFully(final InputStream input, final byte[] array, final int offset, final int len) 238 throws IOException { 239 if (len < 0 || offset < 0 || len + offset > array.length || len + offset < 0) { 240 throw new IndexOutOfBoundsException(); 241 } 242 int count = 0, x = 0; 243 while (count != len) { 244 x = input.read(array, offset + count, len - count); 245 if (x == -1) { 246 break; 247 } 248 count += x; 249 } 250 return count; 251 } 252 253 /** 254 * Reads {@code b.remaining()} bytes from the given channel 255 * starting at the current channel's position. 256 * 257 * <p>This method reads repeatedly from the channel until the 258 * requested number of bytes are read. This method blocks until 259 * the requested number of bytes are read, the end of the channel 260 * is detected, or an exception is thrown.</p> 261 * 262 * @param channel the channel to read from 263 * @param byteBuffer the buffer into which the data is read. 264 * @throws IOException - if an I/O error occurs. 265 * @throws EOFException - if the channel reaches the end before reading all the bytes. 266 */ 267 public static void readFully(final ReadableByteChannel channel, final ByteBuffer byteBuffer) throws IOException { 268 final int expectedLength = byteBuffer.remaining(); 269 int read = 0; 270 while (read < expectedLength) { 271 final int readNow = channel.read(byteBuffer); 272 if (readNow <= 0) { 273 break; 274 } 275 read += readNow; 276 } 277 if (read < expectedLength) { 278 throw new EOFException(); 279 } 280 } 281 282 /** 283 * Gets part of the contents of an {@code InputStream} as a {@code byte[]}. 284 * 285 * @param input the {@code InputStream} to read from 286 * @param len 287 * maximum amount of bytes to copy 288 * @return the requested byte array 289 * @throws NullPointerException if the input is null 290 * @throws IOException if an I/O error occurs 291 * @since 1.21 292 */ 293 public static byte[] readRange(final InputStream input, final int len) throws IOException { 294 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 295 copyRange(input, len, output); 296 return output.toByteArray(); 297 } 298 299 /** 300 * Gets part of the contents of an {@code ReadableByteChannel} as a {@code byte[]}. 301 * 302 * @param input the {@code ReadableByteChannel} to read from 303 * @param len 304 * maximum amount of bytes to copy 305 * @return the requested byte array 306 * @throws NullPointerException if the input is null 307 * @throws IOException if an I/O error occurs 308 * @since 1.21 309 */ 310 public static byte[] readRange(final ReadableByteChannel input, final int len) throws IOException { 311 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 312 final ByteBuffer b = ByteBuffer.allocate(Math.min(len, COPY_BUF_SIZE)); 313 int read = 0; 314 while (read < len) { 315 // Make sure we never read more than len bytes 316 b.limit(Math.min(len - read, b.capacity())); 317 final int readNow = input.read(b); 318 if (readNow <= 0) { 319 break; 320 } 321 output.write(b.array(), 0, readNow); 322 b.rewind(); 323 read += readNow; 324 } 325 return output.toByteArray(); 326 } 327 328 /** 329 * Skips the given number of bytes by repeatedly invoking skip on 330 * the given input stream if necessary. 331 * 332 * <p>In a case where the stream's skip() method returns 0 before 333 * the requested number of bytes has been skip this implementation 334 * will fall back to using the read() method.</p> 335 * 336 * <p>This method will only skip less than the requested number of 337 * bytes if the end of the input stream has been reached.</p> 338 * 339 * @param input stream to skip bytes in 340 * @param numToSkip the number of bytes to skip 341 * @return the number of bytes actually skipped 342 * @throws IOException on error 343 */ 344 public static long skip(final InputStream input, long numToSkip) throws IOException { 345 final long available = numToSkip; 346 while (numToSkip > 0) { 347 final long skipped = input.skip(numToSkip); 348 if (skipped == 0) { 349 break; 350 } 351 numToSkip -= skipped; 352 } 353 354 while (numToSkip > 0) { 355 final int read = readFully(input, SKIP_BUF, 0, 356 (int) Math.min(numToSkip, SKIP_BUF_SIZE)); 357 if (read < 1) { 358 break; 359 } 360 numToSkip -= read; 361 } 362 return available - numToSkip; 363 } 364 365 /** 366 * Gets the contents of an {@code InputStream} as a {@code byte[]}. 367 * <p> 368 * This method buffers the input internally, so there is no need to use a 369 * {@code BufferedInputStream}. 370 * 371 * @param input the {@code InputStream} to read from 372 * @return the requested byte array 373 * @throws NullPointerException if the input is null 374 * @throws IOException if an I/O error occurs 375 * @since 1.5 376 */ 377 public static byte[] toByteArray(final InputStream input) throws IOException { 378 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 379 copy(input, output); 380 return output.toByteArray(); 381 } 382 383 /** Private constructor to prevent instantiation of this utility class. */ 384 private IOUtils(){ 385 } 386 387}