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