using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.IO; namespace AsyncStreaming { /// /// Specifies identifiers to indicate the state of AsyncStreaming. /// public enum AsyncStreamState { None, /// /// This state occurs when stream is ready to start. /// Ready, /// /// This state occurs when stream is started. /// Started, /// /// This state occurs when stream is paused. /// Paused, /// /// This state occurs when stream is stoped. /// Stoped, /// /// This state occurs when stream is finished. /// Finished, /// /// This state occurs when a stream exception is happent. /// Error } /// /// Represents errors that occur during async streaming. /// public class AsyncStreamException : Exception { public AsyncStreamException() : base() { } public AsyncStreamException(string message) : base(message) { } public AsyncStreamException(string message, Exception innerException) : base(message, innerException) { } } #region AsyncStreamStateChangeArgs /// /// Provides data for the AsyncStreaming.AsyncStreamReader class event on state change. /// public class AsyncStreamStateChangeArgs : EventArgs { private AsyncStreamState currentState = AsyncStreamState.Ready; /// /// Gets current object AsyncStream.AsyncStreamReader state. /// public AsyncStreamState CurrentState { get { return currentState; } } /// /// Initializes a new instance of AsyncStreaming.AsyncStreamStateChangeArgs class. /// /// The state that have been changed. public AsyncStreamStateChangeArgs(AsyncStreamState state) { this.currentState = state; } } #endregion #region AsyncStreamErrorEventArgs /// /// Provides data for the AsyncStreaming.AsyncStreamReader and AsyncStreaming.AsyncStreamWriter class event on error. /// public class AsyncStreamErrorEventArgs : EventArgs { private AsyncStreamException error; /// /// Gets AsyncStreamException exception data. /// public AsyncStreamException Error { get { return error; } } /// /// Initializes a new instance of AsyncStreaming.AsyncStreamReader and AsyncStreaming.AsyncStreamWriter class. /// /// AsyncStreamException that have been happen. public AsyncStreamErrorEventArgs(AsyncStreamException e) { error = e; } } #endregion #region AsyncReadEventArgs /// /// Provides data for the AsyncStreaming.AsyncReadEventArgs class event on async read. /// public class AsyncReadEventArgs : EventArgs { private long bytesReaded; /// /// Gets the number of bytes that have been readed. /// public long BytesReaded { get { return bytesReaded; } } private int percentReaded; /// /// Gets percent of current readed bytes. /// public int PercentReaded { get { return percentReaded; } } private long length; /// /// Gets the length in bytes of the stream. /// public long Length { get { return length; } } private bool isComplete; /// /// Gets value that indicating whether the current stream read is complete. /// public bool IsComplete { get { return isComplete; } set { isComplete = value; } } private byte[] result; /// /// Gets current readed strem byte array. /// public byte[] Result { get { return result; } set { result = value; } } /// /// Initializes a new instance of AsyncStreaming.AsyncReadEventArgs class. /// /// How many bytes have been readed till now. /// Total stream length. public AsyncReadEventArgs(long bytesReaded, long length) { this.bytesReaded = bytesReaded; this.length = length; this.isComplete = false; try { //int p = (int)length / 100; this.percentReaded = GetPercent(bytesReaded, length); // (int)bytesReaded / p; if (this.percentReaded > 100) this.percentReaded = 100; } catch (DivideByZeroException) { this.percentReaded = 100; } } public static int GetPercent(long bytesReaded, long length) { int result = 0; try { int p = (int)length / 100; result = (int)bytesReaded / p; } catch (DivideByZeroException) { result = 100; } return result; } /// /// Initializes a new instance of AsyncStreaming.AsyncReadEventArgs class with result when readed is finished. /// /// /// public static AsyncReadEventArgs EndArgs(byte[] result) { AsyncReadEventArgs args = new AsyncReadEventArgs(0, 0); args.IsComplete = true; args.Result = result; return args; } } #endregion #region AsyncWriteEventArgs /// /// Provides data for the AsyncStreaming.AsyncStreamWriter class event on read error. /// public class AsyncWriteEventArgs : EventArgs { private long bytesWrote; /// /// Gets the number of bytes that have been wrote. /// public long BytesWrote { get { return bytesWrote; } } private int percentWrote; /// /// Gets percent of current wrote bytes. /// public int PercentWrote { get { return percentWrote; } } private long length; /// /// Gets the length in bytes of the stream. /// public long Length { get { return length; } } private bool isComplete; /// /// Gets value that indicating whether the current stream write is complete. /// public bool IsComplete { get { return isComplete; } set { isComplete = value; } } /// /// Initializes a new instance of AsyncStreaming.AsyncReadEventArgs class. /// /// How many bytes have been wrote till now. /// Total stream length. public AsyncWriteEventArgs(long bytesWrote, long length) { this.bytesWrote = bytesWrote; this.length = length; this.isComplete = false; try { this.percentWrote = GetPercent(bytesWrote, length); if (this.percentWrote > 100) this.percentWrote = 100; } catch (DivideByZeroException) { this.percentWrote = 100; } } /// /// Initializes a new instance of AsyncStreaming.AsyncWriteEventArgs class with result when write is finished. /// /// /// public static AsyncWriteEventArgs EndArgs() { AsyncWriteEventArgs args = new AsyncWriteEventArgs(0, 0); args.IsComplete = true; return args; } public static int GetPercent(long bytesWrote, long length) { int result = 0; try { int p = (int)bytesWrote * 100; result = p / (int)length + 1; if (result > 100) result = 100; } catch (DivideByZeroException) { result = 0; } return result; } } #endregion [Serializable] public class BaseAsyncStreamReader : Interfaces.IAsyncStreamReader { #region Event declarations /// /// Occurs when the byte is readed from the System.IO.StreamReader. /// public event EventHandler OnReadBytes; /// /// Occurs when the all bytes are readed from the System.IO.StreamReader. /// public event EventHandler OnEndRead; /// /// Occurs when an AsyncStreaming.AsyncExcpetion is happen. /// public event EventHandler OnError; /// /// Occurs when state of AsyncStreaming.AsyncStreamReader is changed. /// public event EventHandler OnStateChanged; #endregion #region Fields protected AsyncStreamState _state = AsyncStreamState.Ready; protected ManualResetEvent _event = new ManualResetEvent(true); protected string _path = String.Empty; protected Thread _worker; protected StreamReader _reader; protected long _streamLenght; protected int _byteBuffer; protected byte[] _buffer; protected int _currentPercent; #endregion #region Properties /// /// Gets the current state of AsyncStreaming.AsyncStreamReader. /// public AsyncStreamState State { get { return _state; } } /// /// Gets the complete file path to be read. /// public string Path { get { return _path; } } #endregion #region Methods /// /// Starts an asynchronous read operation. /// public void StartRead() { if (String.IsNullOrEmpty(_path)) throw new AsyncStreamException("Cannot start read because file path is null or empty!"); _currentPercent = 0; _byteBuffer = 0; _reader = new StreamReader(_path); _streamLenght = _reader.BaseStream.Length; _buffer = new byte[_streamLenght]; _worker = new Thread(new ThreadStart(readWork)); _worker.Start(); } private void readWork() { try { int position = 0; changeState(AsyncStreamState.Started); while (_byteBuffer > -1) { _event.WaitOne(); _byteBuffer = _reader.BaseStream.ReadByte(); if (_byteBuffer != -1) { _buffer[position] = (byte)_byteBuffer; } if (OnReadBytes != null && _byteBuffer != -1) { if (AsyncReadEventArgs.GetPercent(position, _streamLenght) > _currentPercent) { _currentPercent = AsyncReadEventArgs.GetPercent(position, _streamLenght); OnReadBytes(null, new AsyncReadEventArgs(position, _streamLenght)); } } else if (OnEndRead != null && _byteBuffer == -1) { OnEndRead(null, AsyncReadEventArgs.EndArgs(_buffer)); changeState(AsyncStreamState.Finished); _reader.Close(); _reader.Dispose(); _worker.Abort(); } position++; } } catch (ThreadAbortException) { } catch (Exception e) { AsyncStreamException exc = new AsyncStreamException("Error async reading from: " + _path + ".", e); if (OnError != null) { OnError(null, new AsyncStreamErrorEventArgs(exc)); } else throw exc; this.StopRead(); changeState(AsyncStreamState.Error); } } /// /// Pause an asynchronous read operation. /// public void PauseRead() { changeState(AsyncStreamState.Paused); //_worker.Suspend(); _event.Reset(); } /// /// Resume an paused asynchronous read operation. /// public void ResumeRead() { changeState(AsyncStreamState.Started); //_worker.Resume(); _event.Set(); } /// /// Stops an asynchronous read operation. /// public void StopRead() { changeState(AsyncStreamState.Stoped); _currentPercent = 0; if (_worker != null) { _worker.Abort(); _worker = null; } if (_reader != null) { if (_reader.BaseStream != null) { _reader.BaseStream.Flush(); _reader.BaseStream.Close(); } _reader.Close(); _reader.Dispose(); } } private void changeState(AsyncStreamState state) { _state = state; if (OnStateChanged != null) OnStateChanged(null, new AsyncStreamStateChangeArgs(state)); } #endregion #region IDisposable Members public void Dispose() { if (_worker != null) { _worker.Abort(); _worker = null; } if (_reader != null) { if (_reader.BaseStream != null) { _reader.BaseStream.Flush(); _reader.BaseStream.Close(); } _reader.Close(); _reader.Dispose(); } } #endregion } public class AsyncStreamReader : BaseAsyncStreamReader { /// /// Implements a System.IO.TextReader that reads characters from a byte stream /// in a particular encoding. /// /// The complete file path to be read. public AsyncStreamReader(string path) { if (String.IsNullOrEmpty(path)) throw new AsyncStreamException("Cannot create AsyncStreamReader class because file path in null or empty!"); this._path = path; this._currentPercent = 0; } } public class BaseAsyncStreamWriter : Interfaces.IAsyncStreamWriter { #region Events /// /// Occurs when state of AsyncStream.AsyncStreamWriter is changed. /// public event EventHandler OnStateChanged; /// /// Occurs when the byte is writed to the System.IO.StreamWriter. /// public event EventHandler OnWroteBytes; /// /// Occurs when the all bytes are has been written. /// public event EventHandler OnEndWrite; /// /// Occurs when an AsyncStream.AsyncExcpetion is happen. /// public event EventHandler OnError; #endregion #region Fields protected string _outputPath = String.Empty; protected Encoding _streamEncoding = Encoding.Default; protected AsyncStreamState _state = AsyncStreamState.Ready; protected ManualResetEvent _event = new ManualResetEvent(true); protected Thread _worker; protected StreamWriter _writer; protected long _streamLength; protected int _currentPecent; protected byte[] _buffer; protected int _byteBuffer = 0; #endregion #region Properties /// /// Gets the complete file path to write to. Path can be a file name. /// public string OutputPath { get { return _outputPath; } } /// /// Gets or sets the character encoding to use. Default encoding is Encoding.ASCII. /// public Encoding StreamEncoding { get { return _streamEncoding; } set { _streamEncoding = value; } } /// /// Gets the current state of AsyncStreaming.AsyncStreamWriter. /// public AsyncStreamState State { get { return _state; } } #endregion /// /// Begins an asynchronous write operation. /// public void StartWrite() { try { _currentPecent = 0; if (_buffer == null || _buffer.Length == 0) throw new AsyncStreamException("Cannot start write because buffer is null or empty!"); _byteBuffer = 0; _streamLength = _buffer.Length; if (String.IsNullOrEmpty(this._outputPath)) throw new AsyncStreamException("Cannot start write because output file path is null or empty!"); _writer = new StreamWriter(this._outputPath); _worker = new Thread(new ThreadStart(writeWork)); _worker.Start(); } catch (AsyncStreamException aexc) { changeState(AsyncStreamState.Error); if (OnError != null) OnError(null, new AsyncStreamErrorEventArgs(aexc)); else throw aexc; } } private void writeWork() { try { int position = 0; changeState(AsyncStreamState.Started); while (position < _streamLength) { _event.WaitOne(); _writer.BaseStream.WriteByte(_buffer[position]); if (OnWroteBytes != null) { if (AsyncWriteEventArgs.GetPercent(position, _streamLength) > _currentPecent) { _currentPecent = AsyncWriteEventArgs.GetPercent(position, _streamLength); OnWroteBytes(null, new AsyncWriteEventArgs(position, _streamLength)); } } position++; } changeState(AsyncStreamState.Finished); if (OnEndWrite != null) { OnEndWrite(null, AsyncWriteEventArgs.EndArgs()); } this.Dispose(); } catch (ThreadAbortException) { } catch (Exception e) { AsyncStreamException exc = new AsyncStreamException("Error async writing to: " + _outputPath + ".", e); if (OnError != null) { OnError(null, new AsyncStreamErrorEventArgs(exc)); } else throw exc; this.StopWrite(); changeState(AsyncStreamState.Error); } } /// /// Pause an asynchronous write operation. /// public void PauseWrite() { changeState(AsyncStreamState.Paused); //_worker.Suspend(); _event.Reset(); } /// /// Resume an paused asynchronous write operation. /// public void ResumeWrite() { changeState(AsyncStreamState.Started); //_worker.Resume(); _event.Set(); } /// /// Stops an asynchronous write operation. /// public void StopWrite() { changeState(AsyncStreamState.Stoped); this.Dispose(); } /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// An array of bytes. This method copies count bytes from buffer to the current stream. public void SetBuffer(byte[] buffer) { if (buffer == null) throw new AsyncStreamException("Cannot set writer because buffer is null or empty!"); this._buffer = buffer; } /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// The complete file path to write to. Path can be a file name. /// An array of bytes. This method copies count bytes from buffer to the current stream. public void SetWriter(string outputPath, byte[] buffer) { if (buffer == null) throw new AsyncStreamException("Cannot set writer because buffer is null or empty!"); if (String.IsNullOrEmpty(outputPath)) throw new AsyncStreamException("Cannot set writer beaause output file path is null or empty!"); this._outputPath = outputPath; this._buffer = buffer; } /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// The complete file path to write to. Path can be a file name. /// The string to write to the stream. If value is null, nothing is written. public void SetWriter(string outputPath, string buffer) { if (String.IsNullOrEmpty(buffer)) throw new AsyncStreamException("Cannot set writer becuse buffer is null or empty!"); if (String.IsNullOrEmpty(outputPath)) throw new AsyncStreamException("Cannot set writer beause output file path is null or empty!"); this._outputPath = outputPath; this._buffer = _streamEncoding.GetBytes(buffer); } /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// The complete file path to write to. Path can be a file name. /// The string to write to the stream. If value is null, nothing is written. /// The string encoding to use. public void SetWriter(string outputPath, string buffer, Encoding encoding) { if (String.IsNullOrEmpty(buffer)) throw new AsyncStreamException("Cannot set writer because buffer is null or empty!"); if (String.IsNullOrEmpty(outputPath)) throw new AsyncStreamException("Cannot set writer beaause output file path is null or empty!"); if (encoding == null) throw new AsyncStreamException("Cannot set writer because encoding is null!"); this._streamEncoding = encoding; this._outputPath = outputPath; this._buffer = _streamEncoding.GetBytes(buffer); } private void changeState(AsyncStreamState state) { this._state = state; if (OnStateChanged != null) OnStateChanged(null, new AsyncStreamStateChangeArgs(_state)); } #region IDisposable Members public void Dispose() { if (_writer != null) { if (_writer.BaseStream != null) { _writer.BaseStream.Flush(); _writer.BaseStream.Close(); } else { _writer.Close(); _writer.Dispose(); } } if (_worker != null) _worker.Abort(); } #endregion } public class AsyncStreamWriter : BaseAsyncStreamWriter { #region Constructors /// /// Initializes a new empty instance of the AsyncStreaming.AsyncStreamWriter class. /// public AsyncStreamWriter() { } /// /// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified /// file on the specified path, using the default encoding. /// /// The complete file path to write to. Path can be a file name. public AsyncStreamWriter(string outputPath) { this._outputPath = outputPath; } /// /// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified /// file on the specified path, using the default encoding. /// /// The complete file path to write to. Path can be a file name. /// The string to write to the stream. If value is null, nothing is written. /// The string encoding to use. public AsyncStreamWriter(string outputPath, string buffer, Encoding encoding) { if (String.IsNullOrEmpty(buffer)) return; this._outputPath = outputPath; this._buffer = _streamEncoding.GetBytes(buffer); if (encoding != null) this._streamEncoding = encoding; } /// /// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified /// file on the specified path, using the default encoding. /// /// The complete file path to write to. Path can be a file name. /// The string to write to the stream. If value is null, nothing is written. public AsyncStreamWriter(string outputPath, string buffer) { if (String.IsNullOrEmpty(buffer)) return; this._outputPath = outputPath; this._buffer = _streamEncoding.GetBytes(buffer); } /// /// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified /// file on the specified path, using the default encoding. /// /// The complete file path to write to. Path can be a file name. /// An array of bytes. This method copies count bytes from buffer to the current stream. public AsyncStreamWriter(string outputPath, byte[] buffer) { if (buffer == null) return; this._outputPath = outputPath; this._buffer = buffer; } #endregion } } namespace AsyncStreaming.Interfaces { public interface IAsyncStreamReader : IDisposable { /// /// Gets the current state of AsyncStreaming.AsyncStreamReader. /// AsyncStreamState State { get; } /// /// Gets the complete file path to be read. /// string Path { get; } /// /// Starts an asynchronous read operation. /// void StartRead(); /// /// Pause an asynchronous read operation. /// void PauseRead(); /// /// Resume an paused asynchronous read operation. /// void ResumeRead(); /// /// Stops an asynchronous read operation. /// void StopRead(); } public interface IAsyncStreamWriter : IDisposable { /// /// Gets the complete file path to write to. Path can be a file name. /// string OutputPath { get; } /// /// Gets or sets the character encoding to use. Default encoding is Encoding.ASCII. /// Encoding StreamEncoding { get; set; } /// /// Gets the current state of AsyncStreaming.AsyncStreamWriter. /// AsyncStreamState State { get; } /// /// Starts an asynchronous write operation. /// void StartWrite(); /// /// Pause an asynchronous write operation. /// void PauseWrite(); /// /// Resume an paused asynchronous write operation. /// void ResumeWrite(); /// /// Stops an asynchronous write operation. /// void StopWrite(); /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// An array of bytes. This method copies count bytes from buffer to the current stream. void SetBuffer(byte[] buffer); /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// The complete file path to write to. Path can be a file name. /// An array of bytes. This method copies count bytes from buffer to the current stream. void SetWriter(string outputPath, byte[] buffer); /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// The complete file path to write to. Path can be a file name. /// The string to write to the stream. If value is null, nothing is written. void SetWriter(string outputPath, string buffer); /// /// Set AsyncStreaming.AsyncStreamWriter class. /// /// The complete file path to write to. Path can be a file name. /// The string to write to the stream. If value is null, nothing is written. /// The string encoding to use. void SetWriter(string outputPath, string buffer, Encoding encoding); } }