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.FileInputStream; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.OutputStream; 029import java.nio.ByteBuffer; 030import java.nio.channels.ReadableByteChannel; 031 032/** 033 * Utility functions 034 * @Immutable (has mutable data but it is write-only) 035 */ 036public final class IOUtils { 037 038 private static final int COPY_BUF_SIZE = 8024; 039 private static final int SKIP_BUF_SIZE = 4096; 040 041 // This buffer does not need to be synchronised because it is write only; the contents are ignored 042 // Does not affect Immutability 043 private static final byte[] SKIP_BUF = new byte[SKIP_BUF_SIZE]; 044 045 /** Private constructor to prevent instantiation of this utility class. */ 046 private IOUtils(){ 047 } 048 049 /** 050 * Copies the content of a InputStream into an OutputStream. 051 * Uses a default buffer size of 8024 bytes. 052 * 053 * @param input 054 * the InputStream to copy 055 * @param output 056 * the target Stream 057 * @return the number of bytes copied 058 * @throws IOException 059 * if an error occurs 060 */ 061 public static long copy(final InputStream input, final OutputStream output) throws IOException { 062 return copy(input, output, COPY_BUF_SIZE); 063 } 064 065 /** 066 * Copies the content of a InputStream into an OutputStream 067 * 068 * @param input 069 * the InputStream to copy 070 * @param output 071 * the target Stream 072 * @param buffersize 073 * the buffer size to use, must be bigger than 0 074 * @return the number of bytes copied 075 * @throws IOException 076 * if an error occurs 077 * @throws IllegalArgumentException 078 * if buffersize is smaller than or equal to 0 079 */ 080 public static long copy(final InputStream input, final OutputStream output, final int buffersize) throws IOException { 081 if (buffersize < 1) { 082 throw new IllegalArgumentException("buffersize must be bigger than 0"); 083 } 084 final byte[] buffer = new byte[buffersize]; 085 int n = 0; 086 long count=0; 087 while (-1 != (n = input.read(buffer))) { 088 output.write(buffer, 0, n); 089 count += n; 090 } 091 return count; 092 } 093 094 /** 095 * Skips the given number of bytes by repeatedly invoking skip on 096 * the given input stream if necessary. 097 * 098 * <p>In a case where the stream's skip() method returns 0 before 099 * the requested number of bytes has been skip this implementation 100 * will fall back to using the read() method.</p> 101 * 102 * <p>This method will only skip less than the requested number of 103 * bytes if the end of the input stream has been reached.</p> 104 * 105 * @param input stream to skip bytes in 106 * @param numToSkip the number of bytes to skip 107 * @return the number of bytes actually skipped 108 * @throws IOException on error 109 */ 110 public static long skip(final InputStream input, long numToSkip) throws IOException { 111 final long available = numToSkip; 112 while (numToSkip > 0) { 113 final long skipped = input.skip(numToSkip); 114 if (skipped == 0) { 115 break; 116 } 117 numToSkip -= skipped; 118 } 119 120 while (numToSkip > 0) { 121 final int read = readFully(input, SKIP_BUF, 0, 122 (int) Math.min(numToSkip, SKIP_BUF_SIZE)); 123 if (read < 1) { 124 break; 125 } 126 numToSkip -= read; 127 } 128 return available - numToSkip; 129 } 130 131 /** 132 * Reads as much from the file as possible to fill the given array. 133 * 134 * <p>This method may invoke read repeatedly to fill the array and 135 * only read less bytes than the length of the array if the end of 136 * the stream has been reached.</p> 137 * 138 * @param file file to read 139 * @param array buffer to fill 140 * @return the number of bytes actually read 141 * @throws IOException on error 142 * @since 1.20 143 */ 144 public static int read(final File file, final byte[] array) throws IOException { 145 try (FileInputStream inputStream = new FileInputStream(file)) { 146 return readFully(inputStream, array, 0, array.length); 147 } 148 } 149 150 /** 151 * Reads as much from input as possible to fill the given array. 152 * 153 * <p>This method may invoke read repeatedly to fill the array and 154 * only read less bytes than the length of the array if the end of 155 * the stream has been reached.</p> 156 * 157 * @param input stream to read from 158 * @param array buffer to fill 159 * @return the number of bytes actually read 160 * @throws IOException on error 161 */ 162 public static int readFully(final InputStream input, final byte[] array) throws IOException { 163 return readFully(input, array, 0, array.length); 164 } 165 166 /** 167 * Reads as much from input as possible to fill the given array 168 * with the given amount of bytes. 169 * 170 * <p>This method may invoke read repeatedly to read the bytes and 171 * only read less bytes than the requested length if the end of 172 * the stream has been reached.</p> 173 * 174 * @param input stream to read from 175 * @param array buffer to fill 176 * @param offset offset into the buffer to start filling at 177 * @param len of bytes to read 178 * @return the number of bytes actually read 179 * @throws IOException 180 * if an I/O error has occurred 181 */ 182 public static int readFully(final InputStream input, final byte[] array, final int offset, final int len) 183 throws IOException { 184 if (len < 0 || offset < 0 || len + offset > array.length) { 185 throw new IndexOutOfBoundsException(); 186 } 187 int count = 0, x = 0; 188 while (count != len) { 189 x = input.read(array, offset + count, len - count); 190 if (x == -1) { 191 break; 192 } 193 count += x; 194 } 195 return count; 196 } 197 198 /** 199 * Reads {@code b.remaining()} bytes from the given channel 200 * starting at the current channel's position. 201 * 202 * <p>This method reads repeatedly from the channel until the 203 * requested number of bytes are read. This method blocks until 204 * the requested number of bytes are read, the end of the channel 205 * is detected, or an exception is thrown.</p> 206 * 207 * @param channel the channel to read from 208 * @param b the buffer into which the data is read. 209 * @throws IOException - if an I/O error occurs. 210 * @throws EOFException - if the channel reaches the end before reading all the bytes. 211 */ 212 public static void readFully(ReadableByteChannel channel, ByteBuffer b) throws IOException { 213 final int expectedLength = b.remaining(); 214 int read = 0; 215 while (read < expectedLength) { 216 int readNow = channel.read(b); 217 if (readNow <= 0) { 218 break; 219 } 220 read += readNow; 221 } 222 if (read < expectedLength) { 223 throw new EOFException(); 224 } 225 } 226 227 // toByteArray(InputStream) copied from: 228 // commons/proper/io/trunk/src/main/java/org/apache/commons/io/IOUtils.java?revision=1428941 229 // January 8th, 2013 230 // 231 // Assuming our copy() works just as well as theirs! :-) 232 233 /** 234 * Gets the contents of an <code>InputStream</code> as a <code>byte[]</code>. 235 * <p> 236 * This method buffers the input internally, so there is no need to use a 237 * <code>BufferedInputStream</code>. 238 * 239 * @param input the <code>InputStream</code> to read from 240 * @return the requested byte array 241 * @throws NullPointerException if the input is null 242 * @throws IOException if an I/O error occurs 243 * @since 1.5 244 */ 245 public static byte[] toByteArray(final InputStream input) throws IOException { 246 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 247 copy(input, output); 248 return output.toByteArray(); 249 } 250 251 /** 252 * Closes the given Closeable and swallows any IOException that may occur. 253 * @param c Closeable to close, can be null 254 * @since 1.7 255 */ 256 public static void closeQuietly(final Closeable c) { 257 if (c != null) { 258 try { 259 c.close(); 260 } catch (final IOException ignored) { // NOPMD NOSONAR 261 } 262 } 263 } 264}