|
|
|
@ -1,12 +1,24 @@ |
|
|
|
|
package me.ag2s.epublib.util; |
|
|
|
|
|
|
|
|
|
import java.io.ByteArrayOutputStream; |
|
|
|
|
import java.io.Closeable; |
|
|
|
|
import java.io.EOFException; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.io.InputStream; |
|
|
|
|
import java.io.InputStreamReader; |
|
|
|
|
import java.io.OutputStream; |
|
|
|
|
import java.io.OutputStreamWriter; |
|
|
|
|
import java.io.Reader; |
|
|
|
|
import java.io.StringWriter; |
|
|
|
|
import java.io.Writer; |
|
|
|
|
import java.net.HttpURLConnection; |
|
|
|
|
import java.net.URLConnection; |
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
import java.nio.CharBuffer; |
|
|
|
|
import java.nio.channels.ReadableByteChannel; |
|
|
|
|
import java.nio.charset.Charset; |
|
|
|
|
|
|
|
|
|
import me.ag2s.epublib.util.commons.io.IOConsumer; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Most of the functions herein are re-implementations of the ones in |
|
|
|
@ -23,8 +35,22 @@ public class IOUtil { |
|
|
|
|
*/ |
|
|
|
|
public static final int EOF = -1; |
|
|
|
|
|
|
|
|
|
public static final int IO_COPY_BUFFER_SIZE = 1024 * 8; |
|
|
|
|
public static final int DEFAULT_BUFFER_SIZE = 8192; |
|
|
|
|
|
|
|
|
|
public static final int DEFAULT_BUFFER_SIZE = 1024*8; |
|
|
|
|
private static final byte[] SKIP_BYTE_BUFFER = new byte[DEFAULT_BUFFER_SIZE]; |
|
|
|
|
|
|
|
|
|
// Allocated in the relevant skip method if necessary.
|
|
|
|
|
/* |
|
|
|
|
* These buffers are static and are shared between threads. |
|
|
|
|
* This is possible because the buffers are write-only - the contents are never read. |
|
|
|
|
* |
|
|
|
|
* N.B. there is no need to synchronize when creating these because: |
|
|
|
|
* - we don't care if the buffer is created multiple times (the data is ignored) |
|
|
|
|
* - we always use the same size buffer, so if it it is recreated it will still be OK |
|
|
|
|
* (if the buffer size were variable, we would need to synch. to ensure some other thread |
|
|
|
|
* did not create a smaller one) |
|
|
|
|
*/ |
|
|
|
|
private static char[] SKIP_CHAR_BUFFER; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Gets the contents of the Reader as a byte[], with the given character encoding. |
|
|
|
@ -51,7 +77,7 @@ public class IOUtil { |
|
|
|
|
*/ |
|
|
|
|
public static byte[] toByteArray(InputStream in) throws IOException { |
|
|
|
|
ByteArrayOutputStream result = new ByteArrayOutputStream(); |
|
|
|
|
copy(in, result); |
|
|
|
|
copy(in, result,DEFAULT_BUFFER_SIZE); |
|
|
|
|
result.flush(); |
|
|
|
|
return result.toByteArray(); |
|
|
|
|
} |
|
|
|
@ -79,7 +105,7 @@ public class IOUtil { |
|
|
|
|
result = new ByteArrayOutputStream(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
copy(in, result); |
|
|
|
|
copy(in, result,DEFAULT_BUFFER_SIZE); |
|
|
|
|
result.flush(); |
|
|
|
|
return result.toByteArray(); |
|
|
|
|
} catch (OutOfMemoryError error) { |
|
|
|
@ -112,45 +138,695 @@ public class IOUtil { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
/** |
|
|
|
|
* Copies the contents of the InputStream to the OutputStream. |
|
|
|
|
* Copies bytes from an <code>InputStream</code> to an <code>OutputStream</code> using an internal buffer of the |
|
|
|
|
* given size. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param in f |
|
|
|
|
* @param out f |
|
|
|
|
* @return the nr of bytes read, or -1 if the amount > Integer.MAX_VALUE |
|
|
|
|
* @throws IOException f |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @param bufferSize the bufferSize used to copy from the input to the output |
|
|
|
|
* @return the number of bytes copied. or {@code 0} if {@code input is null}. |
|
|
|
|
* @throws NullPointerException if the output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.5 |
|
|
|
|
*/ |
|
|
|
|
public static int copy(InputStream in, OutputStream out) |
|
|
|
|
public static long copy(final InputStream input, final OutputStream output, final int bufferSize) |
|
|
|
|
throws IOException { |
|
|
|
|
byte[] buffer = new byte[IO_COPY_BUFFER_SIZE]; |
|
|
|
|
int readSize; |
|
|
|
|
int result = 0; |
|
|
|
|
while ((readSize = in.read(buffer)) >= 0) { |
|
|
|
|
out.write(buffer, 0, readSize); |
|
|
|
|
result = calcNewNrReadSize(readSize, result); |
|
|
|
|
return copyLarge(input, output, new byte[bufferSize]); |
|
|
|
|
} |
|
|
|
|
out.flush(); |
|
|
|
|
return result; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies bytes from an <code>InputStream</code> to chars on a |
|
|
|
|
* <code>Writer</code> using the default character encoding of the platform. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses {@link InputStreamReader}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 1.1 |
|
|
|
|
* @deprecated 2.5 use {@link #copy(InputStream, Writer, Charset)} instead |
|
|
|
|
*/ |
|
|
|
|
@Deprecated |
|
|
|
|
public static void copy(final InputStream input, final Writer output) |
|
|
|
|
throws IOException { |
|
|
|
|
copy(input, output, Charset.defaultCharset()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies the contents of the Reader to the Writer. |
|
|
|
|
* Copies bytes from an <code>InputStream</code> to chars on a |
|
|
|
|
* <code>Writer</code> using the specified character encoding. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses {@link InputStreamReader}. |
|
|
|
|
* |
|
|
|
|
* @param in f |
|
|
|
|
* @param out f |
|
|
|
|
* @return the nr of characters read, or -1 if the amount > Integer.MAX_VALUE |
|
|
|
|
* @throws IOException f |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @param inputCharset the charset to use for the input stream, null means platform default |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.3 |
|
|
|
|
*/ |
|
|
|
|
public static int copy(Reader in, Writer out) throws IOException { |
|
|
|
|
char[] buffer = new char[IO_COPY_BUFFER_SIZE]; |
|
|
|
|
int readSize; |
|
|
|
|
int result = 0; |
|
|
|
|
while ((readSize = in.read(buffer)) >= 0) { |
|
|
|
|
out.write(buffer, 0, readSize); |
|
|
|
|
result = calcNewNrReadSize(readSize, result); |
|
|
|
|
public static void copy(final InputStream input, final Writer output, final Charset inputCharset) |
|
|
|
|
throws IOException { |
|
|
|
|
final InputStreamReader in = new InputStreamReader(input, inputCharset.name()); |
|
|
|
|
copy(in, output); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies bytes from an <code>InputStream</code> to chars on a |
|
|
|
|
* <code>Writer</code> using the specified character encoding. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* <p> |
|
|
|
|
* Character encoding names can be found at |
|
|
|
|
* <a href="http://www.iana.org/assignments/character-sets">IANA</a>. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses {@link InputStreamReader}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @param inputCharsetName the name of the requested charset for the InputStream, null means platform default |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link java.io |
|
|
|
|
* .UnsupportedEncodingException} in version 2.2 if the |
|
|
|
|
* encoding is not supported. |
|
|
|
|
* @since 1.1 |
|
|
|
|
*/ |
|
|
|
|
public static void copy(final InputStream input, final Writer output, final String inputCharsetName) |
|
|
|
|
throws IOException { |
|
|
|
|
copy(input, output,Charset.forName(inputCharsetName)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a <code>Reader</code> to a <code>Appendable</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* Large streams (over 2GB) will return a chars copied value of |
|
|
|
|
* <code>-1</code> after the copy has completed since the correct |
|
|
|
|
* number of chars cannot be returned as an int. For large streams |
|
|
|
|
* use the <code>copyLarge(Reader, Writer)</code> method. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Appendable</code> to write to |
|
|
|
|
* @return the number of characters copied, or -1 if > Integer.MAX_VALUE |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.7 |
|
|
|
|
*/ |
|
|
|
|
public static long copy(final Reader input, final Appendable output) throws IOException { |
|
|
|
|
return copy(input, output, CharBuffer.allocate(DEFAULT_BUFFER_SIZE)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a <code>Reader</code> to an <code>Appendable</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses the provided buffer, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Appendable</code> to write to |
|
|
|
|
* @param buffer the buffer to be used for the copy |
|
|
|
|
* @return the number of characters copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.7 |
|
|
|
|
*/ |
|
|
|
|
public static long copy(final Reader input, final Appendable output, final CharBuffer buffer) throws IOException { |
|
|
|
|
long count = 0; |
|
|
|
|
int n; |
|
|
|
|
while (EOF != (n = input.read(buffer))) { |
|
|
|
|
buffer.flip(); |
|
|
|
|
output.append(buffer, 0, n); |
|
|
|
|
count += n; |
|
|
|
|
} |
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a <code>Reader</code> to bytes on an |
|
|
|
|
* <code>OutputStream</code> using the default character encoding of the |
|
|
|
|
* platform, and calling flush. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* Due to the implementation of OutputStreamWriter, this method performs a |
|
|
|
|
* flush. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses {@link OutputStreamWriter}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 1.1 |
|
|
|
|
* @deprecated 2.5 use {@link #copy(Reader, OutputStream, Charset)} instead |
|
|
|
|
*/ |
|
|
|
|
@Deprecated |
|
|
|
|
public static void copy(final Reader input, final OutputStream output) |
|
|
|
|
throws IOException { |
|
|
|
|
copy(input, output, Charset.defaultCharset()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a <code>Reader</code> to bytes on an |
|
|
|
|
* <code>OutputStream</code> using the specified character encoding, and |
|
|
|
|
* calling flush. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* </p> |
|
|
|
|
* <p> |
|
|
|
|
* Due to the implementation of OutputStreamWriter, this method performs a |
|
|
|
|
* flush. |
|
|
|
|
* </p> |
|
|
|
|
* <p> |
|
|
|
|
* This method uses {@link OutputStreamWriter}. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @param outputCharset the charset to use for the OutputStream, null means platform default |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.3 |
|
|
|
|
*/ |
|
|
|
|
public static void copy(final Reader input, final OutputStream output, final Charset outputCharset) |
|
|
|
|
throws IOException { |
|
|
|
|
final OutputStreamWriter out = new OutputStreamWriter(output, outputCharset.name()); |
|
|
|
|
copy(input, out); |
|
|
|
|
// XXX Unless anyone is planning on rewriting OutputStreamWriter,
|
|
|
|
|
// we have to flush here.
|
|
|
|
|
out.flush(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a <code>Reader</code> to bytes on an |
|
|
|
|
* <code>OutputStream</code> using the specified character encoding, and |
|
|
|
|
* calling flush. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* Character encoding names can be found at |
|
|
|
|
* <a href="http://www.iana.org/assignments/character-sets">IANA</a>. |
|
|
|
|
* <p> |
|
|
|
|
* Due to the implementation of OutputStreamWriter, this method performs a |
|
|
|
|
* flush. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses {@link OutputStreamWriter}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @param outputCharsetName the name of the requested charset for the OutputStream, null means platform default |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link java.io |
|
|
|
|
* .UnsupportedEncodingException} in version 2.2 if the |
|
|
|
|
* encoding is not supported. |
|
|
|
|
* @since 1.1 |
|
|
|
|
*/ |
|
|
|
|
public static void copy(final Reader input, final OutputStream output, final String outputCharsetName) |
|
|
|
|
throws IOException { |
|
|
|
|
copy(input, output, Charset.forName(outputCharsetName)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a <code>Reader</code> to a <code>Writer</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* Large streams (over 2GB) will return a chars copied value of |
|
|
|
|
* <code>-1</code> after the copy has completed since the correct |
|
|
|
|
* number of chars cannot be returned as an int. For large streams |
|
|
|
|
* use the <code>copyLarge(Reader, Writer)</code> method. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @return the number of characters copied, or -1 if > Integer.MAX_VALUE |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 1.1 |
|
|
|
|
*/ |
|
|
|
|
public static int copy(final Reader input, final Writer output) throws IOException { |
|
|
|
|
final long count = copyLarge(input, output); |
|
|
|
|
if (count > Integer.MAX_VALUE) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
return (int) count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an |
|
|
|
|
* <code>OutputStream</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* </p> |
|
|
|
|
* <p> |
|
|
|
|
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @return the number of bytes copied. or {@code 0} if {@code input is null}. |
|
|
|
|
* @throws NullPointerException if the output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 1.3 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final InputStream input, final OutputStream output) |
|
|
|
|
throws IOException { |
|
|
|
|
return copy(input, output, DEFAULT_BUFFER_SIZE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an |
|
|
|
|
* <code>OutputStream</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses the provided buffer, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @param buffer the buffer to use for the copy |
|
|
|
|
* @return the number of bytes copied. or {@code 0} if {@code input is null}. |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.2 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer) |
|
|
|
|
throws IOException { |
|
|
|
|
long count = 0; |
|
|
|
|
if (input != null) { |
|
|
|
|
int n; |
|
|
|
|
while (EOF != (n = input.read(buffer))) { |
|
|
|
|
output.write(buffer, 0, n); |
|
|
|
|
count += n; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies some or all bytes from a large (over 2GB) <code>InputStream</code> to an |
|
|
|
|
* <code>OutputStream</code>, optionally skipping input bytes. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* </p> |
|
|
|
|
* <p> |
|
|
|
|
* Note that the implementation uses {@link #skip(InputStream, long)}. |
|
|
|
|
* This means that the method may be considerably less efficient than using the actual skip implementation, |
|
|
|
|
* this is done to guarantee that the correct number of characters are skipped. |
|
|
|
|
* </p> |
|
|
|
|
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @param inputOffset : number of bytes to skip from input before copying |
|
|
|
|
* -ve values are ignored |
|
|
|
|
* @param length : number of bytes to copy. -ve means all |
|
|
|
|
* @return the number of bytes copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.2 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final InputStream input, final OutputStream output, final long inputOffset, |
|
|
|
|
final long length) throws IOException { |
|
|
|
|
return copyLarge(input, output, inputOffset, length, new byte[DEFAULT_BUFFER_SIZE]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies some or all bytes from a large (over 2GB) <code>InputStream</code> to an |
|
|
|
|
* <code>OutputStream</code>, optionally skipping input bytes. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses the provided buffer, so there is no need to use a |
|
|
|
|
* <code>BufferedInputStream</code>. |
|
|
|
|
* </p> |
|
|
|
|
* <p> |
|
|
|
|
* Note that the implementation uses {@link #skip(InputStream, long)}. |
|
|
|
|
* This means that the method may be considerably less efficient than using the actual skip implementation, |
|
|
|
|
* this is done to guarantee that the correct number of characters are skipped. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>InputStream</code> to read from |
|
|
|
|
* @param output the <code>OutputStream</code> to write to |
|
|
|
|
* @param inputOffset : number of bytes to skip from input before copying |
|
|
|
|
* -ve values are ignored |
|
|
|
|
* @param length : number of bytes to copy. -ve means all |
|
|
|
|
* @param buffer the buffer to use for the copy |
|
|
|
|
* @return the number of bytes copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.2 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final InputStream input, final OutputStream output, |
|
|
|
|
final long inputOffset, final long length, final byte[] buffer) throws IOException { |
|
|
|
|
if (inputOffset > 0) { |
|
|
|
|
skipFully(input, inputOffset); |
|
|
|
|
} |
|
|
|
|
if (length == 0) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
final int bufferLength = buffer.length; |
|
|
|
|
int bytesToRead = bufferLength; |
|
|
|
|
if (length > 0 && length < bufferLength) { |
|
|
|
|
bytesToRead = (int) length; |
|
|
|
|
} |
|
|
|
|
int read; |
|
|
|
|
long totalRead = 0; |
|
|
|
|
while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) { |
|
|
|
|
output.write(buffer, 0, read); |
|
|
|
|
totalRead += read; |
|
|
|
|
if (length > 0) { // only adjust length if not reading to the end
|
|
|
|
|
// Note the cast must work because buffer.length is an integer
|
|
|
|
|
bytesToRead = (int) Math.min(length - totalRead, bufferLength); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return totalRead; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @return the number of characters copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 1.3 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final Reader input, final Writer output) throws IOException { |
|
|
|
|
return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses the provided buffer, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @param buffer the buffer to be used for the copy |
|
|
|
|
* @return the number of characters copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.2 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final Reader input, final Writer output, final char[] buffer) throws IOException { |
|
|
|
|
long count = 0; |
|
|
|
|
int n; |
|
|
|
|
while (EOF != (n = input.read(buffer))) { |
|
|
|
|
output.write(buffer, 0, n); |
|
|
|
|
count += n; |
|
|
|
|
} |
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies some or all chars from a large (over 2GB) <code>InputStream</code> to an |
|
|
|
|
* <code>OutputStream</code>, optionally skipping input chars. |
|
|
|
|
* <p> |
|
|
|
|
* This method buffers the input internally, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @param inputOffset : number of chars to skip from input before copying |
|
|
|
|
* -ve values are ignored |
|
|
|
|
* @param length : number of chars to copy. -ve means all |
|
|
|
|
* @return the number of chars copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.2 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final Reader input, final Writer output, final long inputOffset, final long length) |
|
|
|
|
throws IOException { |
|
|
|
|
return copyLarge(input, output, inputOffset, length, new char[DEFAULT_BUFFER_SIZE]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Copies some or all chars from a large (over 2GB) <code>InputStream</code> to an |
|
|
|
|
* <code>OutputStream</code>, optionally skipping input chars. |
|
|
|
|
* <p> |
|
|
|
|
* This method uses the provided buffer, so there is no need to use a |
|
|
|
|
* <code>BufferedReader</code>. |
|
|
|
|
* <p> |
|
|
|
|
* |
|
|
|
|
* @param input the <code>Reader</code> to read from |
|
|
|
|
* @param output the <code>Writer</code> to write to |
|
|
|
|
* @param inputOffset : number of chars to skip from input before copying |
|
|
|
|
* -ve values are ignored |
|
|
|
|
* @param length : number of chars to copy. -ve means all |
|
|
|
|
* @param buffer the buffer to be used for the copy |
|
|
|
|
* @return the number of chars copied |
|
|
|
|
* @throws NullPointerException if the input or output is null |
|
|
|
|
* @throws IOException if an I/O error occurs |
|
|
|
|
* @since 2.2 |
|
|
|
|
*/ |
|
|
|
|
public static long copyLarge(final Reader input, final Writer output, final long inputOffset, final long length, |
|
|
|
|
final char[] buffer) |
|
|
|
|
throws IOException { |
|
|
|
|
if (inputOffset > 0) { |
|
|
|
|
skipFully(input, inputOffset); |
|
|
|
|
} |
|
|
|
|
if (length == 0) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
int bytesToRead = buffer.length; |
|
|
|
|
if (length > 0 && length < buffer.length) { |
|
|
|
|
bytesToRead = (int) length; |
|
|
|
|
} |
|
|
|
|
int read; |
|
|
|
|
long totalRead = 0; |
|
|
|
|
while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) { |
|
|
|
|
output.write(buffer, 0, read); |
|
|
|
|
totalRead += read; |
|
|
|
|
if (length > 0) { // only adjust length if not reading to the end
|
|
|
|
|
// Note the cast must work because buffer.length is an integer
|
|
|
|
|
bytesToRead = (int) Math.min(length - totalRead, buffer.length); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return totalRead; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Skips bytes from an input byte stream. |
|
|
|
|
* This implementation guarantees that it will read as many bytes |
|
|
|
|
* as possible before giving up; this may not always be the case for |
|
|
|
|
* skip() implementations in subclasses of {@link InputStream}. |
|
|
|
|
* <p> |
|
|
|
|
* Note that the implementation uses {@link InputStream#read(byte[], int, int)} rather |
|
|
|
|
* than delegating to {@link InputStream#skip(long)}. |
|
|
|
|
* This means that the method may be considerably less efficient than using the actual skip implementation, |
|
|
|
|
* this is done to guarantee that the correct number of bytes are skipped. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input byte stream to skip |
|
|
|
|
* @param toSkip number of bytes to skip. |
|
|
|
|
* @return number of bytes actually skipped. |
|
|
|
|
* @throws IOException if there is a problem reading the file |
|
|
|
|
* @throws IllegalArgumentException if toSkip is negative |
|
|
|
|
* @see InputStream#skip(long) |
|
|
|
|
* @see <a href="https://issues.apache.org/jira/browse/IO-203">IO-203 - Add skipFully() method for InputStreams</a> |
|
|
|
|
* @since 2.0 |
|
|
|
|
*/ |
|
|
|
|
public static long skip(final InputStream input, final long toSkip) throws IOException { |
|
|
|
|
if (toSkip < 0) { |
|
|
|
|
throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); |
|
|
|
|
} |
|
|
|
|
/* |
|
|
|
|
* N.B. no need to synchronize access to SKIP_BYTE_BUFFER: - we don't care if the buffer is created multiple |
|
|
|
|
* times (the data is ignored) - we always use the same size buffer, so if it it is recreated it will still be |
|
|
|
|
* OK (if the buffer size were variable, we would need to synch. to ensure some other thread did not create a |
|
|
|
|
* smaller one) |
|
|
|
|
*/ |
|
|
|
|
long remain = toSkip; |
|
|
|
|
while (remain > 0) { |
|
|
|
|
// See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
|
|
|
|
|
final long n = input.read(SKIP_BYTE_BUFFER, 0, (int) Math.min(remain, SKIP_BYTE_BUFFER.length)); |
|
|
|
|
if (n < 0) { // EOF
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
remain -= n; |
|
|
|
|
} |
|
|
|
|
return toSkip - remain; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Skips bytes from a ReadableByteChannel. |
|
|
|
|
* This implementation guarantees that it will read as many bytes |
|
|
|
|
* as possible before giving up. |
|
|
|
|
* |
|
|
|
|
* @param input ReadableByteChannel to skip |
|
|
|
|
* @param toSkip number of bytes to skip. |
|
|
|
|
* @return number of bytes actually skipped. |
|
|
|
|
* @throws IOException if there is a problem reading the ReadableByteChannel |
|
|
|
|
* @throws IllegalArgumentException if toSkip is negative |
|
|
|
|
* @since 2.5 |
|
|
|
|
*/ |
|
|
|
|
public static long skip(final ReadableByteChannel input, final long toSkip) throws IOException { |
|
|
|
|
if (toSkip < 0) { |
|
|
|
|
throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); |
|
|
|
|
} |
|
|
|
|
final ByteBuffer skipByteBuffer = ByteBuffer.allocate((int) Math.min(toSkip, SKIP_BYTE_BUFFER.length)); |
|
|
|
|
long remain = toSkip; |
|
|
|
|
while (remain > 0) { |
|
|
|
|
skipByteBuffer.position(0); |
|
|
|
|
skipByteBuffer.limit((int) Math.min(remain, SKIP_BYTE_BUFFER.length)); |
|
|
|
|
final int n = input.read(skipByteBuffer); |
|
|
|
|
if (n == EOF) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
remain -= n; |
|
|
|
|
} |
|
|
|
|
return toSkip - remain; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Skips characters from an input character stream. |
|
|
|
|
* This implementation guarantees that it will read as many characters |
|
|
|
|
* as possible before giving up; this may not always be the case for |
|
|
|
|
* skip() implementations in subclasses of {@link Reader}. |
|
|
|
|
* <p> |
|
|
|
|
* Note that the implementation uses {@link Reader#read(char[], int, int)} rather |
|
|
|
|
* than delegating to {@link Reader#skip(long)}. |
|
|
|
|
* This means that the method may be considerably less efficient than using the actual skip implementation, |
|
|
|
|
* this is done to guarantee that the correct number of characters are skipped. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input character stream to skip |
|
|
|
|
* @param toSkip number of characters to skip. |
|
|
|
|
* @return number of characters actually skipped. |
|
|
|
|
* @throws IOException if there is a problem reading the file |
|
|
|
|
* @throws IllegalArgumentException if toSkip is negative |
|
|
|
|
* @see Reader#skip(long) |
|
|
|
|
* @see <a href="https://issues.apache.org/jira/browse/IO-203">IO-203 - Add skipFully() method for InputStreams</a> |
|
|
|
|
* @since 2.0 |
|
|
|
|
*/ |
|
|
|
|
public static long skip(final Reader input, final long toSkip) throws IOException { |
|
|
|
|
if (toSkip < 0) { |
|
|
|
|
throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); |
|
|
|
|
} |
|
|
|
|
/* |
|
|
|
|
* N.B. no need to synchronize this because: - we don't care if the buffer is created multiple times (the data |
|
|
|
|
* is ignored) - we always use the same size buffer, so if it it is recreated it will still be OK (if the buffer |
|
|
|
|
* size were variable, we would need to synch. to ensure some other thread did not create a smaller one) |
|
|
|
|
*/ |
|
|
|
|
if (SKIP_CHAR_BUFFER == null) { |
|
|
|
|
SKIP_CHAR_BUFFER = new char[SKIP_BYTE_BUFFER.length]; |
|
|
|
|
} |
|
|
|
|
long remain = toSkip; |
|
|
|
|
while (remain > 0) { |
|
|
|
|
// See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
|
|
|
|
|
final long n = input.read(SKIP_CHAR_BUFFER, 0, (int) Math.min(remain, SKIP_BYTE_BUFFER.length)); |
|
|
|
|
if (n < 0) { // EOF
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
remain -= n; |
|
|
|
|
} |
|
|
|
|
return toSkip - remain; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Skips the requested number of bytes or fail if there are not enough left. |
|
|
|
|
* <p> |
|
|
|
|
* This allows for the possibility that {@link InputStream#skip(long)} may |
|
|
|
|
* not skip as many bytes as requested (most likely because of reaching EOF). |
|
|
|
|
* <p> |
|
|
|
|
* Note that the implementation uses {@link #skip(InputStream, long)}. |
|
|
|
|
* This means that the method may be considerably less efficient than using the actual skip implementation, |
|
|
|
|
* this is done to guarantee that the correct number of characters are skipped. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input stream to skip |
|
|
|
|
* @param toSkip the number of bytes to skip |
|
|
|
|
* @throws IOException if there is a problem reading the file |
|
|
|
|
* @throws IllegalArgumentException if toSkip is negative |
|
|
|
|
* @throws EOFException if the number of bytes skipped was incorrect |
|
|
|
|
* @see InputStream#skip(long) |
|
|
|
|
* @since 2.0 |
|
|
|
|
*/ |
|
|
|
|
public static void skipFully(final InputStream input, final long toSkip) throws IOException { |
|
|
|
|
if (toSkip < 0) { |
|
|
|
|
throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip); |
|
|
|
|
} |
|
|
|
|
final long skipped = skip(input, toSkip); |
|
|
|
|
if (skipped != toSkip) { |
|
|
|
|
throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Skips the requested number of bytes or fail if there are not enough left. |
|
|
|
|
* |
|
|
|
|
* @param input ReadableByteChannel to skip |
|
|
|
|
* @param toSkip the number of bytes to skip |
|
|
|
|
* @throws IOException if there is a problem reading the ReadableByteChannel |
|
|
|
|
* @throws IllegalArgumentException if toSkip is negative |
|
|
|
|
* @throws EOFException if the number of bytes skipped was incorrect |
|
|
|
|
* @since 2.5 |
|
|
|
|
*/ |
|
|
|
|
public static void skipFully(final ReadableByteChannel input, final long toSkip) throws IOException { |
|
|
|
|
if (toSkip < 0) { |
|
|
|
|
throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip); |
|
|
|
|
} |
|
|
|
|
final long skipped = skip(input, toSkip); |
|
|
|
|
if (skipped != toSkip) { |
|
|
|
|
throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Skips the requested number of characters or fail if there are not enough left. |
|
|
|
|
* <p> |
|
|
|
|
* This allows for the possibility that {@link Reader#skip(long)} may |
|
|
|
|
* not skip as many characters as requested (most likely because of reaching EOF). |
|
|
|
|
* <p> |
|
|
|
|
* Note that the implementation uses {@link #skip(Reader, long)}. |
|
|
|
|
* This means that the method may be considerably less efficient than using the actual skip implementation, |
|
|
|
|
* this is done to guarantee that the correct number of characters are skipped. |
|
|
|
|
* </p> |
|
|
|
|
* |
|
|
|
|
* @param input stream to skip |
|
|
|
|
* @param toSkip the number of characters to skip |
|
|
|
|
* @throws IOException if there is a problem reading the file |
|
|
|
|
* @throws IllegalArgumentException if toSkip is negative |
|
|
|
|
* @throws EOFException if the number of characters skipped was incorrect |
|
|
|
|
* @see Reader#skip(long) |
|
|
|
|
* @since 2.0 |
|
|
|
|
*/ |
|
|
|
|
public static void skipFully(final Reader input, final long toSkip) throws IOException { |
|
|
|
|
final long skipped = skip(input, toSkip); |
|
|
|
|
if (skipped != toSkip) { |
|
|
|
|
throw new EOFException("Chars to skip: " + toSkip + " actual: " + skipped); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
* Returns the length of the given array in a null-safe manner. |
|
|
|
@ -195,6 +871,65 @@ public class IOUtil { |
|
|
|
|
public static int length(final Object[] array) { |
|
|
|
|
return array == null ? 0 : array.length; |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
* Closes the given {@link Closeable} as a null-safe operation. |
|
|
|
|
* |
|
|
|
|
* @param closeable The resource to close, may be null. |
|
|
|
|
* @throws IOException if an I/O error occurs. |
|
|
|
|
* @since 2.7 |
|
|
|
|
*/ |
|
|
|
|
public static void close(final Closeable closeable) throws IOException { |
|
|
|
|
if (closeable != null) { |
|
|
|
|
closeable.close(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Closes the given {@link Closeable} as a null-safe operation. |
|
|
|
|
* |
|
|
|
|
* @param closeables The resource(s) to close, may be null. |
|
|
|
|
* @throws IOException if an I/O error occurs. |
|
|
|
|
* @since 2.8.0 |
|
|
|
|
*/ |
|
|
|
|
public static void close(final Closeable... closeables) throws IOException { |
|
|
|
|
if (closeables != null) { |
|
|
|
|
for (final Closeable closeable : closeables) { |
|
|
|
|
close(closeable); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Closes the given {@link Closeable} as a null-safe operation. |
|
|
|
|
* |
|
|
|
|
* @param closeable The resource to close, may be null. |
|
|
|
|
* @param consumer Consume the IOException thrown by {@link Closeable#close()}. |
|
|
|
|
* @throws IOException if an I/O error occurs. |
|
|
|
|
* @since 2.7 |
|
|
|
|
*/ |
|
|
|
|
public static void close(final Closeable closeable, final IOConsumer<IOException> consumer) throws IOException { |
|
|
|
|
if (closeable != null) { |
|
|
|
|
try { |
|
|
|
|
closeable.close(); |
|
|
|
|
} catch (final IOException e) { |
|
|
|
|
if (consumer != null) { |
|
|
|
|
consumer.accept(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Closes a URLConnection. |
|
|
|
|
* |
|
|
|
|
* @param conn the connection to close. |
|
|
|
|
* @since 2.4 |
|
|
|
|
*/ |
|
|
|
|
public static void close(final URLConnection conn) { |
|
|
|
|
if (conn instanceof HttpURLConnection) { |
|
|
|
|
((HttpURLConnection) conn).disconnect(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unused") |
|
|
|
|
public static String Stream2String(InputStream inputStream) { |
|
|
|
|