#region License and Copyright
/* -------------------------------------------------------------------------
* Dotnet Commons IO
*
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the
*
* Free Software Foundation, Inc.,
* 59 Temple Place,
* Suite 330,
* Boston,
* MA 02111-1307
* USA
*
* -------------------------------------------------------------------------
*/
#endregion
using System;
using System.IO;
using System.Text;
namespace Dotnet.Commons.IO
{
/// <summary>
/// Utility class that provides methods to manipulate stream of data.
/// </summary>
/// <remarks>
/// This class is ported from <a href="http://jakarta.apache.org/commons/io/">Jakarta Commons IO</a> org.apache.commons.io.CopyUtils class.
///
/// This class also contains code taken from an article written by Jon Skeet. The
/// article can be found here: <a href="http://www.developerfusion.co.uk/show/4696/">
/// http://www.developerfusion.co.uk/show/4696/</a>
/// </remarks>
public sealed class StreamUtils
{
/// <summary> The name says it all.</summary>
private const int DEFAULT_BUFFER_SIZE = 1024 * 4;
private StreamUtils(){}
public static void Copy(byte[] input, byte[] output, long outputOffset)
{
if (input.Length == 0) return;
for (int i=0; i<input.Length; i++)
{
output[outputOffset + i] = input[i];
}
}
/// <summary> Copy bytes from a <see cref="byte"/>[] to an Output <see cref="Stream"/>.</summary>
/// <param name="input">the byte array to read from
/// </param>
/// <param name="output">the Output <see cref="Stream"/> to write to
/// </param>
/// <exception cref="IOException">if an I/O problem occurs</exception>
public static void Copy(byte[] input, Stream output)
{
if (input.Length == 0) return;
output.Write(input, 0, input.Length);
}
/// <summary>
/// Copy and convert bytes from a <see cref="byte"/>[] to chars on a
/// <see cref="StreamWriter"/>.
/// </summary>
/// <param name="input">the byte array to read from</param>
/// <param name="outputWriter">the <see cref="StreamWriter"/> to write to</param>
/// <exception cref="IOException">in the case of an I/O problem</exception>
public static void Copy(byte[] input, StreamWriter outputWriter)
{
MemoryStream inputStream = new MemoryStream(input);
Copy(inputStream, outputWriter);
}
/// <summary>
/// Copy and convert bytes from a <see cref="byte"/>[] to chars on a
/// <see cref="StreamWriter"/>.
/// </summary>
/// <param name="input"></param>
/// <param name="outputWriter"></param>
/// <param name="encoding"></param>
public static void Copy(byte[] input, StreamWriter outputWriter, string encoding)
{
MemoryStream inputStream = new MemoryStream(input);
Copy(inputStream, outputWriter, encoding);
}
/// <summary> Copy the entire content from an Input <see cref="Stream"/> to an Output <see cref="Stream"/>.</summary>
/// <param name="input">the Input <see cref="Stream"/>to read from the beginning of the stream
/// </param>
/// <param name="output">the Output <see cref="Stream"/> to write to
/// </param>
/// <returns> the number of bytes copied
/// </returns>
/// <exception cref="IOException">if an I/O problem occurs</exception>
public static int Copy(Stream input, Stream output)
{
return Copy(input, output, false);
}
/// <summary> Copy bytes from an Input <see cref="Stream"/> to an Output <see cref="Stream"/>.</summary>
/// <param name="input">the Input <see cref="Stream"/>to read from
/// </param>
/// <param name="output">the Output <see cref="Stream"/> to write to
/// </param>
/// <param name="copyFromBeginning">Set true to copy from the beginning of the input stream, eg. input.Position=0,
/// otherwise, it will start copying from whatever the current position in the input stream.
/// </param>
/// <returns> the number of bytes copied
/// </returns>
/// <exception cref="IOException">if an I/O problem occurs</exception>
public static int Copy(Stream input, Stream output, bool copyFromBeginning)
{
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int bytesRead = 0;
int len = DEFAULT_BUFFER_SIZE;
int offset=0;
if (copyFromBeginning)
input.Seek(0, SeekOrigin.Begin);
// set it to the beginning
while (len > 0)
{
len = input.Read(buffer, offset, DEFAULT_BUFFER_SIZE);
output.Write(buffer, 0, len);
bytesRead += len;
}
return bytesRead;
}
// ----------------------------------------------------------------
// Reader -> Writer
// ----------------------------------------------------------------
/// <summary> Copy chars from a <see cref="StreamReader"/> to a <see cref="StreamWriter"/>.</summary>
/// <param name="inputStreamReader">the <see cref="StreamReader"/> to read from
/// </param>
/// <param name="outputStreamWriter">the <see cref="StreamWriter"/> to write to
/// </param>
/// <returns> the number of characters copied
/// </returns>
/// <exception cref="IOException">if an I/O problem occurs</exception>
public static int Copy(StreamReader inputStreamReader, StreamWriter outputStreamWriter)
{
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
int count = 0;
int len = DEFAULT_BUFFER_SIZE;
while (len > 0)
{
len = inputStreamReader.Read(buffer, 0, DEFAULT_BUFFER_SIZE);
outputStreamWriter.Write(buffer, 0, len);
count += len;
}
return count;
}
// ----------------------------------------------------------------
// InputStream -> Writer
// ----------------------------------------------------------------
/// <summary> Copy and convert bytes from an Input <see cref="Stream"/> to chars on a
/// <see cref="StreamWriter"/>.
/// The platform's default encoding is used for the byte-to-char conversion.
/// </summary>
/// <param name="inputStream">the Input <see cref="Stream"/> to read from
/// </param>
/// <param name="outputStreamWriter">the <see cref="StreamWriter"/>to write to
/// </param>
/// <exception cref="IOException">if an I/O problem occurs</exception>
public static int Copy(Stream inputStream, StreamWriter outputStreamWriter)
{
StreamReader inputStreamReader = new StreamReader(inputStream, System.Text.Encoding.Default);
return Copy(inputStreamReader, outputStreamWriter);
}
/// <summary>
/// Copy and convert bytes from an Input <see cref="Stream"/> to chars on a
/// <see cref="StreamWriter"/>, using the specified encoding.
/// </summary>
/// <param name="inputStream"></param>
/// <param name="outputWriter"></param>
/// <param name="encoding">The name of a supported character encoding. See the
/// <a href="http://www.iana.org/assignments/character-sets">IANA
/// Charset Registry</a> and <a href="http://msdn2.microsoft.com/en-us/library/system.text.encoding.aspx">MSDN: Encoding class</a>
/// for a list of valid encoding types.</param>
/// <exception cref="IOException">an I/O problem occurs</exception>
public static int Copy(Stream inputStream, StreamWriter outputWriter, String encoding)
{
Encoding encode = Encoding.Default;
try
{
encode = Encoding.GetEncoding(encoding);
}
catch
{
encode = Encoding.Default;
}
StreamReader inputStreamReader = new StreamReader(inputStream, encode);
return Copy(inputStreamReader, outputWriter);
}
// ----------------------------------------------------------------
// Reader -> OutputStream
// ----------------------------------------------------------------
/// <summary>
/// Serialize chars from a <see cref="StreamReader"/> to bytes on an
/// Output <see cref="Stream"/>, and flush the Output <see cref="Stream"/>.
/// </summary>
/// <param name="inputReader">the <see cref="StreamReader"/> to read from</param>
/// <param name="output">the <see cref="Stream"/> to write to</param>
/// <exception cref="IOException">an I/O problem occurs</exception>
public static void Copy(StreamReader inputReader, Stream output)
{
StreamWriter outputWriter = new StreamWriter(output, System.Text.Encoding.Default);
Copy(inputReader, outputWriter);
outputWriter.Flush();
}
// ----------------------------------------------------------------
// String -> OutputStream
// ----------------------------------------------------------------
/// <summary> Serialize chars from a <see cref="String"/> to bytes on an
/// Output <see cref="Stream"/>, and
/// flush the Output <see cref="Stream"/>.
/// </summary>
/// <param name="input">the <see cref="String"/> to read from
/// </param>
/// <param name="output">the Output <see cref="Stream"/> to write to
/// </param>
/// <exception cref="IOException">an I/O problem occurs</exception>
public static void Copy(String input, Stream output)
{
byte[] inputByteArray = new ASCIIEncoding().GetBytes(input);
StreamWriter outWriter = new StreamWriter(output, System.Text.Encoding.Default);
Copy(inputByteArray, outWriter);
outWriter.Flush();
}
// ----------------------------------------------------------------
// String -> Writer
// ----------------------------------------------------------------
/// <summary> Copy chars from a <see cref="String"/> to a <see cref="StreamWriter"/>.</summary>
/// <param name="input">the <see cref="String"/> to read from
/// </param>
/// <param name="output">the <see cref="StreamWriter"/> to write to
/// </param>
/// <exception cref="IOException">an I/O problem occurs</exception>
public static void Copy(String input, StreamWriter output)
{
output.Write(input);
}
/// <summary>
/// Copy the exact number of bytes from the source <see cref="Stream"/> to a
/// target <see cref="Stream"/>.
/// </summary>
/// <param name="source">Source <see cref="Stream"/> to copy from</param>
/// <param name="target">Target <see cref="Stream"/> to copy to</param>
/// <param name="len">number of bytes to copy</param>
/// <exception cref="IOException">if the source stream does not have enough data.</exception>
public static void CopyExact(Stream source, Stream target, int len)
{
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int bytesRead = 0;
while (bytesRead < len)
{
int sizeNeeded = Math.Min(buffer.Length, len - bytesRead);
int readSize = source.Read(buffer, 0, sizeNeeded);
if (readSize <= 0)
throw new IOException(String.Format("Underlying stream does not have enough data. Read {0} bytes, but {1} needed", readSize, sizeNeeded));
target.Write(buffer, 0, readSize);
bytesRead += readSize;
}
}
/// <summary>
/// Reads data into a complete array, throwing an EndOfStreamException
/// if the stream runs out of data first, or if an IOException
/// naturally occurs.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="byteArray">The array to read bytes into. The array
/// will be completely filled from the stream, so an appropriate
/// size must be given.</param>
public static void ReadIntoByteArray (Stream stream, byte[] byteArray)
{
int offset=0;
int remaining = byteArray.Length;
stream.Position = 0;
while (remaining > 0)
{
int read = stream.Read(byteArray, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
/// <summary>
/// Reads data from the beginning of a stream until the end is reached. The
/// data is returned as a byte array.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <exception cref="IOException">thrown if any of the underlying IO calls fail</exception>
/// <remarks>Use this method if you don't know the length of the stream in advance
/// (for instance a network stream) and just want to read the whole lot into a buffer.
/// <para>
/// <strong>Note:</strong><br/>
/// This method of reading the stream is not terribly efficient.
/// </para>
/// </remarks>
public static byte[] GetBytes(Stream stream)
{
if (stream is MemoryStream)
return ((MemoryStream)stream).ToArray();
byte[] byteArray = new byte[DEFAULT_BUFFER_SIZE];
using (MemoryStream ms = new MemoryStream())
{
stream.Position = 0;
while (true)
{
int readLen = stream.Read (byteArray, 0, byteArray.Length);
if (readLen <= 0)
return ms.ToArray();
ms.Write (byteArray, 0, readLen);
}
}
}
/// <summary>
/// Reads data from a stream until the end is reached. The
/// data is returned as a byte array.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="initialLength">The initial buffer length. If the length is < 1,
/// then the default value of <see cref="Int16.MaxValue"/> will be used.
/// </param>
/// <exception cref="IOException">thrown if any of the underlying IO calls fail</exception>
/// <remarks>Use this method to get the data if you know the expected length of data to start with.</remarks>
public static byte[] GetBytes (Stream stream, long initialLength)
{
// If we've been passed an unhelpful initial length, just
// use 32K.
if (initialLength < 1)
initialLength = Int16.MaxValue;
byte[] buffer = new byte[initialLength];
int read=0;
int chunk;
while ( (chunk = stream.Read(buffer, read, buffer.Length-read)) > 0)
{
read += chunk;
// If we've reached the end of our buffer, check to see if there's
// any more information
if (read == buffer.Length)
{
int nextByte = stream.ReadByte();
// End of stream? If so, we're done
if (nextByte==-1)
{
return buffer;
}
// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte[] newBuffer = new byte[buffer.Length*2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read]=(byte)nextByte;
buffer = newBuffer;
read++;
}
}
// Buffer is now too big. Shrink it.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}
/// <summary>
/// Return an ASCII string from a stream of data
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public static string GetAsciiString(Stream stream)
{
ASCIIEncoding encoding = new ASCIIEncoding();
return GetString(stream, encoding);
}
/// <summary>
/// Return an UTF8 encoded string from a stream of data
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public static string GetString(Stream stream)
{
UTF8Encoding encoding = new UTF8Encoding();
return GetString(stream, encoding);
}
/// <summary>
/// Return a string from a stream. The string is returned with
/// the encoding provided.
/// </summary>
/// <param name="stream"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string GetString(Stream stream, Encoding encoding)
{
if (stream == null)
return string.Empty;
byte[] bytes = new byte[stream.Length];
if (stream is MemoryStream)
bytes = ((MemoryStream)stream).GetBuffer();
else
ReadIntoByteArray(stream, bytes);
return encoding.GetString(bytes);
}
/// <summary>
/// Reads the specified number of bytes from any position in a source stream into a
/// specific byte array in a specific start index position. The byte
/// array must have the necessary size to read the portion of the stream required.
/// </summary>
/// <param name="source">Source stream to read from</param>
/// <param name="target">Target byte array to write to</param>
/// <param name="targetOffset">offset index in the target</param>
/// <param name="sourceOffset">offset position in the stream</param>
/// <param name="bytesToRead">number of bytes to read in the stream</param>
/// <exception cref="ArgumentException">thrown if the target byte array is too small to stored
/// the required number of bytes read from the stream.</exception>
public static void ReadExact(Stream source,
byte[] target,
int sourceOffset,
int targetOffset,
int bytesToRead)
{
if (targetOffset + bytesToRead > target.Length)
throw new ArgumentException("target array to small");
int bytesRead = 0;
source.Seek(sourceOffset, SeekOrigin.Begin);
while (bytesRead < bytesToRead)
{ // need more data
int sizeNeeded = Math.Min(DEFAULT_BUFFER_SIZE, bytesToRead - bytesRead);
// read either the whole buffer length or
// the remaining # of bytes: bytesToRead - sizeNeeded
int readSize = source.Read(target, (targetOffset + bytesRead), sizeNeeded);
if (readSize <= 0)
throw new IOException(String.Format("Underlying stream does not have enough data. Read {0} bytes, but {1} needed", readSize, sizeNeeded));
bytesRead += readSize;
}
}
/// <summary>
/// Read a partial segment of a stream, starting from an offset position.
/// </summary>
/// <param name="source">Source stream to read from</param>
/// <param name="sourceOffset">the starting offset position in the stream. Set to 0 if the stream is to be read from the beginning.</param>
/// <param name="bytesToRead">number of bytes to read</param>
/// <returns>return partial segment as an array of bytes.</returns>
public static byte[] ReadPartial(Stream source,
long sourceOffset,
long bytesToRead)
{
long sizeDiff = source.Length - sourceOffset;
if (bytesToRead > sizeDiff)
throw new ArgumentException("Bytes required exceeds what is available in stream");
byte[] target = new byte[bytesToRead];
long bytesRead = 0;
source.Seek(sourceOffset, SeekOrigin.Begin);
while (bytesRead < bytesToRead)
{ // need more data
int sizeNeeded = (int)Math.Min((long)DEFAULT_BUFFER_SIZE, bytesToRead - bytesRead);
// read either the whole buffer length or
// the remaining # of bytes: bytesToRead - sizeNeeded
int readSize = source.Read(target, 0, sizeNeeded);
if (readSize <= 0)
throw new IOException(String.Format("Underlying stream does not have enough data. Read {0} bytes, but {1} needed", readSize, sizeNeeded));
bytesRead += readSize;
}
return target;
}
/// <summary>
/// Read a stream (like a file or HttpWebRequest) and write to another stream
/// </summary>
/// <param name="readStream">the stream to read</param>
/// <param name="writeStream"> the stream to write to</param>
[Obsolete("See StreamUtils.Copy")]
public static void ReadWriteStream(Stream readStream, Stream writeStream)
{
Byte[] buffer = new Byte[DEFAULT_BUFFER_SIZE];
int bytesRead = readStream.Read(buffer, 0, DEFAULT_BUFFER_SIZE);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, DEFAULT_BUFFER_SIZE);
}
readStream.Close();
writeStream.Close();
}
/// <summary>
/// Try to skip bytes in the input stream and return the actual number of bytes skipped.
/// </summary>
/// <param name="stream">Input stream that will be used to skip the bytes</param>
/// <param name="skipBytes">Number of bytes to be skipped</param>
/// <returns>Actual number of bytes skipped</returns>
public static int Skip(Stream stream, int skipBytes)
{
long oldPosition = stream.Position;
long result = stream.Seek(skipBytes, SeekOrigin.Current) - oldPosition;
return (int)result;
}
/// <summary>
/// Skips a given number of characters into a given Stream.
/// </summary>
/// <param name=" stream">The stream in which the skips are done.</param>
/// <param name="number">The number of caracters to skip.</param>
/// <returns>The number of characters skipped.</returns>
public static long Skip(StreamReader stream, long number)
{
long skippedBytes = 0;
for (long index = 0; index < number; index++)
{
stream.Read();
skippedBytes++;
}
return skippedBytes;
}
/// <summary>
/// Skips a given number of characters into a given StringReader.
/// </summary>
/// <param name="strReader">The StringReader in which the skips are done.</param>
/// <param name="number">The number of caracters to skip.</param>
/// <returns>The number of characters skipped.</returns>
public static long Skip(StringReader strReader, long number)
{
long skippedBytes = 0;
for (long index = 0; index < number; index++)
{
strReader.Read();
skippedBytes++;
}
return skippedBytes;
}
/// <summary>
/// Converts a string to an array of bytes
/// </summary>
/// <param name="sourceString">The string to be converted</param>
/// <returns>The new array of bytes</returns>
public static byte[] ToByteArray(String sourceString)
{
return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString);
}
/// <summary>
/// Converts a array of object-type instances to a byte-type array.
/// </summary>
/// <param name="tempObjectArray">Array to convert.</param>
/// <returns>An array of byte type elements.</returns>
public static byte[] ToByteArray(Object[] tempObjectArray)
{
byte[] byteArray = null;
if (tempObjectArray != null)
{
byteArray = new byte[tempObjectArray.Length];
for (int index = 0; index < tempObjectArray.Length; index++)
byteArray[index] = (byte)tempObjectArray[index];
}
return byteArray;
}
}
}
|