IO-305: New copyLarge() method in IOUtils that takes additional offset, length arguments
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/io/trunk@1301851 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 960365c..6e61fe8 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -24,6 +24,7 @@
Changes in this version include:
New features:
+o IO-305: New copyLarge() method in IOUtils that takes additional offset, length arguments Thanks to Manoj Mokashi.
o IO-287: Use terabyte (TB) , petabyte (PB) and exabyte (EB) in FileUtils.byteCountToDisplaySize(long size) Thanks to Ron Kuris, Gary Gregory.
o IO-173: FileUtils.listFiles() doesn't return directories Thanks to Marcos Vinícius da Silva.
o IO-297: CharSequenceInputStream to efficiently stream content of a CharSequence Thanks to Oleg Kalnichevski.
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7868227..c6aee9d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -40,6 +40,9 @@
<body>
<release version="2.2" date="TBA">
+ <action issue="IO-305" dev="sebb" type="add" due-to="Manoj Mokashi">
+ New copyLarge() method in IOUtils that takes additional offset, length arguments
+ </action>
<action issue="IO-300" dev="sebb" type="fix">
FileUtils.moveDirectoryToDirectory removes source directory if destination is a subdirectory
</action>
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java
index 1645c5b..a269223 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -1458,6 +1458,51 @@
}
/**
+ * Copy 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>
+ * 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 offset : 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 Commons IO 2.2
+ */
+ public static long copyLarge(InputStream input, OutputStream output, final long offset, final long length)
+ throws IOException {
+ if( offset > 0){
+ skipFully( input, offset);
+ }
+ if (length == 0) {
+ return 0;
+ }
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int bytesToRead = buffer.length;
+ if (length > 0 && length < buffer.length) {
+ bytesToRead = (int) length;
+ }
+ int read;
+ long totalRead = 0;
+ while(bytesToRead > 0 && -1 != (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;
+ }
+
+ /**
* Copy bytes from an <code>InputStream</code> to chars on a
* <code>Writer</code> using the default character encoding of the platform.
* <p>
@@ -1562,6 +1607,51 @@
}
/**
+ * Copy 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 offset : 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 Commons IO 2.2
+ */
+ public static long copyLarge(Reader input, Writer output, final long offset, final long length)
+ throws IOException {
+ if( offset > 0){
+ skipFully( input, offset);
+ }
+ if (length == 0) {
+ return 0;
+ }
+ char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+ int bytesToRead = buffer.length;
+ if (length > 0 && length < buffer.length) {
+ bytesToRead = (int) length;
+ }
+ int read;
+ long totalRead = 0;
+ while(bytesToRead > 0 && -1 != (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;
+ }
+
+ /**
* Copy chars from a <code>Reader</code> to bytes on an
* <code>OutputStream</code> using the default character encoding of the
* platform, and calling flush.
diff --git a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java
index 942aed0..2369332 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java
@@ -17,7 +17,9 @@
package org.apache.commons.io;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
@@ -73,10 +75,23 @@
throw new RuntimeException("Can't run this test because " + "environment could not be built: "
+ ioe.getMessage());
}
+ // Create and init a byte array as input data
+ iarr = new byte[200];
+ Arrays.fill( iarr, (byte)-1);
+ for( int i=0; i< 80; i++){
+ iarr[i] = (byte) i;
+ }
+ carr = new char[200];
+ Arrays.fill( carr, (char)-1);
+ for( int i=0; i< 80; i++){
+ carr[i] = (char) i;
+ }
}
@Override
public void tearDown() {
+ carr = null;
+ iarr = null;
try {
FileUtils.deleteDirectory(getTestDirectory());
} catch (IOException ioe) {
@@ -718,4 +733,259 @@
IOUtils.closeQuietly(input);
}
+ // Tests from IO-305
+
+ private byte[] iarr = null;
+
+ public void testNoSkip() throws IOException {
+ ByteArrayInputStream is = null;
+ ByteArrayOutputStream os = null;
+ try {
+ // Create streams
+ is = new ByteArrayInputStream( iarr);
+ os = new ByteArrayOutputStream();
+
+ // Test our copy method
+ assertEquals(100, IOUtils.copyLarge( is, os, 0, 100));
+ byte[] oarr = os.toByteArray();
+
+ // check that output length is correct
+ assertEquals( 100, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 1, oarr[1] );
+ assertEquals( 79, oarr[79] );
+ assertEquals( -1, oarr[80] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testSkip() throws IOException {
+ ByteArrayInputStream is = null;
+ ByteArrayOutputStream os = null;
+ try {
+ // Create streams
+ is = new ByteArrayInputStream( iarr);
+ os = new ByteArrayOutputStream();
+
+ // Test our copy method
+ assertEquals(100, IOUtils.copyLarge( is, os, 10, 100));
+ byte[] oarr = os.toByteArray();
+
+ // check that output length is correct
+ assertEquals( 100, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 11, oarr[1] );
+ assertEquals( 79, oarr[69] );
+ assertEquals( -1, oarr[70] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testSkipInvalid() throws IOException {
+ ByteArrayInputStream is = null;
+ ByteArrayOutputStream os = null;
+ try {
+ // Create streams
+ is = new ByteArrayInputStream( iarr);
+ os = new ByteArrayOutputStream();
+
+ // Test our copy method
+ IOUtils.copyLarge( is, os, 1000, 100);
+ fail( "Should have thrown EOFException");
+ }
+ catch( EOFException eofe){
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testFullLength() throws IOException {
+ ByteArrayInputStream is = null;
+ ByteArrayOutputStream os = null;
+ try {
+ // Create streams
+ is = new ByteArrayInputStream( iarr);
+ os = new ByteArrayOutputStream();
+
+ // Test our copy method
+ assertEquals(200, IOUtils.copyLarge( is, os, 0, -1));
+ byte[] oarr = os.toByteArray();
+
+ // check that output length is correct
+ assertEquals( 200, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 1, oarr[1] );
+ assertEquals( 79, oarr[79] );
+ assertEquals( -1, oarr[80] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testExtraLength() throws IOException {
+ ByteArrayInputStream is = null;
+ ByteArrayOutputStream os = null;
+ try {
+ // Create streams
+ is = new ByteArrayInputStream( iarr);
+ os = new ByteArrayOutputStream();
+
+ // Test our copy method
+ // for extra length, it reads till EOF
+ assertEquals(200, IOUtils.copyLarge( is, os, 0, 2000));
+ byte[] oarr = os.toByteArray();
+
+ // check that output length is correct
+ assertEquals( 200, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 1, oarr[1] );
+ assertEquals( 79, oarr[79] );
+ assertEquals( -1, oarr[80] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ private char[] carr = null;
+
+ public void testCharNoSkip() throws IOException {
+ CharArrayReader is = null;
+ CharArrayWriter os = null;
+ try {
+ // Create streams
+ is = new CharArrayReader( carr);
+ os = new CharArrayWriter();
+
+ // Test our copy method
+ assertEquals(100, IOUtils.copyLarge( is, os, 0, 100));
+ char[] oarr = os.toCharArray();
+
+ // check that output length is correct
+ assertEquals( 100, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 1, oarr[1] );
+ assertEquals( 79, oarr[79] );
+ assertEquals((char) -1, oarr[80] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testCharSkip() throws IOException {
+ CharArrayReader is = null;
+ CharArrayWriter os = null;
+ try {
+ // Create streams
+ is = new CharArrayReader( carr);
+ os = new CharArrayWriter();
+
+ // Test our copy method
+ assertEquals(100, IOUtils.copyLarge( is, os, 10, 100));
+ char[] oarr = os.toCharArray();
+
+ // check that output length is correct
+ assertEquals( 100, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 11, oarr[1] );
+ assertEquals( 79, oarr[69] );
+ assertEquals((char) -1, oarr[70] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testCharSkipInvalid() throws IOException {
+ CharArrayReader is = null;
+ CharArrayWriter os = null;
+ try {
+ // Create streams
+ is = new CharArrayReader( carr);
+ os = new CharArrayWriter();
+
+ // Test our copy method
+ IOUtils.copyLarge( is, os, 1000, 100);
+ fail( "Should have thrown EOFException");
+ }
+ catch( EOFException eofe){
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testCharFullLength() throws IOException {
+ CharArrayReader is = null;
+ CharArrayWriter os = null;
+ try {
+ // Create streams
+ is = new CharArrayReader( carr);
+ os = new CharArrayWriter();
+
+ // Test our copy method
+ assertEquals(200, IOUtils.copyLarge( is, os, 0, -1));
+ char[] oarr = os.toCharArray();
+
+ // check that output length is correct
+ assertEquals( 200, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 1, oarr[1] );
+ assertEquals( 79, oarr[79] );
+ assertEquals((char) -1, oarr[80] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
+
+ public void testCharExtraLength() throws IOException {
+ CharArrayReader is = null;
+ CharArrayWriter os = null;
+ try {
+ // Create streams
+ is = new CharArrayReader( carr);
+ os = new CharArrayWriter();
+
+ // Test our copy method
+ // for extra length, it reads till EOF
+ assertEquals(200, IOUtils.copyLarge( is, os, 0, 2000));
+ char[] oarr = os.toCharArray();
+
+ // check that output length is correct
+ assertEquals( 200, oarr.length );
+ // check that output data corresponds to input data
+ assertEquals( 1, oarr[1] );
+ assertEquals( 79, oarr[79] );
+ assertEquals((char) -1, oarr[80] );
+
+ }
+ finally {
+ IOUtils.closeQuietly(is);
+ IOUtils.closeQuietly(os);
+ }
+ }
}