예제 #1
0
        //TODO Look into more efficient methods https://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp/
        private void TransferFiles(TransferBase transfer, long bufferSize = 65535)
        {
            string file         = null;
            Stream sourceStream = null;
            Stream destStream   = null;
            Action <string, Stream, Stream> finishMethod = null;
            Action <long> updateMethod   = null;
            long          totalBytesRead = 0;
            int           readLength     = 0;

            byte[] ActiveBuffer = new byte[bufferSize];
            byte[] BackBuffer   = new byte[bufferSize];
            bool   readStarted  = false;
            //var transfer = _activeTransfer;
            IAsyncResult readState = null;

            do
            {
                if (!readStarted)
                {
                    transfer.GetNextFile(out file, out sourceStream, out destStream, out finishMethod, out updateMethod, out totalBytesRead);
                }
                if (file != null)
                {
                    var thisFile         = file;
                    var thisSourceStream = sourceStream;
                    var thisDestStream   = destStream;
                    var thisFinishMethod = finishMethod;
                    var thisUpdateMethod = updateMethod;

                    transfer.Status = Interfaces.TransferStatus.TransferingFiles;
                    long fileLength = 0;
                    if (sourceStream.CanSeek)
                    {
                        fileLength = sourceStream.Length;
                    }
                    bool endOfFile = false;
                    if (!readStarted)
                    {
                        //start reading asynchronously
                        readState   = sourceStream.BeginRead(ActiveBuffer, 0, ActiveBuffer.Length, null, null);
                        readStarted = true;
                    }
                    do
                    {
                        readLength      = sourceStream.EndRead(readState);
                        readStarted     = false;
                        totalBytesRead += readLength;

                        //If we haven't reached the end of the file...
                        if (readLength != 0)
                        {
                            var thisTotalBytesRead = totalBytesRead;

                            bool pauseStream = false;
                            bool abortStream = false;
                            //Kick off another read asynchronously while we write.
                            try
                            {
                                if (fileLength > 0 && sourceStream.Position >= fileLength)
                                {
                                    endOfFile = true;
                                }
                                else if (ShouldAbortTransfer(transfer))
                                {
                                    abortStream = true;
                                }
                                //If the streams can be paused, and the manager is being paused or this transfer isn't first anymore, then pause the streams and return.
                                else if (transfer.CanPauseMidStream() && (_IsPaused || (transfer != GetFirstTransfer(preferCurrentTransfer: !transfer.CanCopy))))    //If this transfer became locked, mark it preferred because we should probably close the streams before pausing
                                {
                                    //Flag the stream for pausing
                                    pauseStream = true;
                                }
                                else
                                {
                                    readState   = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);
                                    readStarted = true;
                                }
                            }
                            catch (IOException)
                            {
                                endOfFile = true;
                            }
                            if (endOfFile && !abortStream && !pauseStream)
                            {
                                transfer.GetNextFile(out file, out sourceStream, out destStream, out finishMethod, out updateMethod, out totalBytesRead);
                                if (file != null)
                                {
                                    readState   = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);
                                    readStarted = true;
                                }
                                else
                                {
                                    file         = thisFile;
                                    sourceStream = thisSourceStream;
                                    destStream   = thisDestStream;
                                }
                            }
                            //Write the active buffer to the destination, update progress, and do a buffer swap.
                            thisDestStream.Write(ActiveBuffer, 0, readLength);
                            thisUpdateMethod(thisTotalBytesRead);
                            BackBuffer = Interlocked.Exchange(ref ActiveBuffer, BackBuffer);
                            if (endOfFile == false)
                            {
                                //If there is an abort request, dispose of our active streams and bail out. The fact that we dispose here and nowhere else is a little dirty...
                                if (abortStream)
                                {
                                    try
                                    {
                                        sourceStream.Dispose();
                                    }
                                    catch (Exception ex)
                                    {
                                        Utils.Logging.Logger.Error("An exception occurred while aborting the current file source stream:", ex);
                                    }
                                    try
                                    {
                                        destStream.Dispose();
                                    }
                                    catch (Exception ex)
                                    {
                                        Utils.Logging.Logger.Error("An exception occurred while aborting the current file destination stream:", ex);
                                    }
                                    return;
                                }
                                else if (pauseStream)
                                {
                                    transfer.PauseStreaming(thisFile, sourceStream, destStream, thisTotalBytesRead);
                                    return;
                                }
                            }
                        }
                    }while ((readLength != 0) && (endOfFile == false));    //Break out once there is no more to read

                    if (thisFinishMethod != null)
                    {
                        thisFinishMethod(thisFile, thisSourceStream, thisDestStream); //Callback to the finish method,
                    }
                }

                if (_IsPaused || (transfer != GetFirstTransfer()))
                {
                    transfer.Pause();
                    return;
                }
            } while (file != null); //If file is null, we've reached the end
            Steam.SteamBase.UiDispatcher.Invoke((Action)(() =>
            {
                lock (_transferLock)    //Acquire the transfer lock
                {
                    Transfers.Remove(transfer);
                }
            }));
            Steam.SteamBase.UiDispatcher.BeginInvoke((Action)(() => transfer.RunPostProcessing()));
        }
예제 #2
0
        //TODO Look into more efficient methods https://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp/
        private void TransferFiles(TransferBase transfer, long bufferSize = 65535)
        {
            string file         = null;
            long   fileSize     = 0;
            Stream sourceStream = null;
            Stream destStream   = null;
            Action <string, Stream, Stream> finishMethod = null;
            Action <long> updateMethod   = null;
            long          totalBytesRead = 0;
            int           readLength     = 0;

            byte[] ActiveBuffer = new byte[bufferSize];
            byte[] BackBuffer   = new byte[bufferSize];
            bool   readStarted  = false;
            //var transfer = _activeTransfer;
            IAsyncResult readState = null;

            do
            {
                if (!readStarted)
                {
                    transfer.GetNextFile(out file, out fileSize, out sourceStream, out destStream, out finishMethod, out updateMethod, out totalBytesRead);
                }
                if (file != null)
                {
                    var thisFile         = file;
                    var thisSourceStream = sourceStream;
                    var thisDestStream   = destStream;
                    var thisFinishMethod = finishMethod;
                    var thisUpdateMethod = updateMethod;

                    transfer.Status = Interfaces.TransferStatus.TransferingFiles;
                    long fileLength = 0;
                    //Local file streams can check the file length, however network streams cannot, in which case we read until we get a read length of 0
                    if (sourceStream.CanSeek)
                    {
                        fileLength = sourceStream.Length;
                    }


                    bool endOfFile = false;

                    //If we already started a read while waiting for the final write on the previous file, then skip this block
                    if (!readStarted)
                    {
                        try
                        {
                            //start reading asynchronously
                            readState   = sourceStream.BeginRead(ActiveBuffer, 0, ActiveBuffer.Length, null, null);
                            readStarted = true;
                        }
                        catch (IOException ex)
                        {
                            SafeDisposeStream(sourceStream);
                            SafeDisposeStream(destStream);
                            readStarted = false;
                            Utils.Logging.Logger.Error($"Exception reading from {file}: {ex.Message}. A retry will be attempted.");
                            transfer.RetryFile(file, fileSize);
                        }
                    }
                    if (readStarted)
                    {
                        bool      readFailedMidStream          = false;
                        Exception readFailedMidStreamException = null;
                        long      readFailedMidStreamFileSize  = 0;
                        //Loop until we reach the end of file
                        do
                        {
                            readLength      = sourceStream.EndRead(readState);
                            readStarted     = false;
                            totalBytesRead += readLength;

                            //If we haven't reached the end of the file...
                            if (readLength != 0)
                            {
                                var thisTotalBytesRead = totalBytesRead;
                                readFailedMidStream = false;
                                bool pauseStream = false;
                                bool abortStream = false;
                                if (fileLength > 0 && sourceStream.Position >= fileLength)
                                {
                                    endOfFile = true;
                                }
                                else if (ShouldAbortTransfer(transfer))
                                {
                                    abortStream = true;
                                }
                                //If the streams can be paused, and the manager is being paused or this transfer isn't first anymore, then pause the streams and return.
                                else if (transfer.CanPauseMidStream() && (_IsPaused || (transfer != GetFirstTransfer(preferCurrentTransfer: !transfer.GetCanCopyCached()))))    //If this transfer became locked, mark it preferred because we should probably close the streams before pausing
                                {
                                    //Flag the stream for pausing
                                    pauseStream = true;
                                }
                                else
                                {
                                    //Kick off another read to run asynchronously while we write.
                                    try
                                    {
                                        readState   = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);
                                        readStarted = true;
                                    }
                                    catch (IOException ex)
                                    {
                                        endOfFile                    = true;
                                        readFailedMidStream          = true;
                                        readFailedMidStreamException = ex;
                                        readFailedMidStreamFileSize  = fileSize;
                                    }
                                }

                                if (endOfFile && !abortStream && !pauseStream)
                                {
                                    bool tryAgain = false;
                                    do
                                    {
                                        transfer.GetNextFile(out file, out fileSize, out sourceStream, out destStream, out finishMethod, out updateMethod, out totalBytesRead);
                                        if (file != null)
                                        {
                                            try
                                            {
                                                readState   = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);
                                                readStarted = true;
                                                tryAgain    = false;
                                            }
                                            catch (IOException ex)
                                            {
                                                SafeDisposeStream(sourceStream);
                                                SafeDisposeStream(destStream);
                                                readStarted = false;
                                                Utils.Logging.Logger.Error($"Exception reading from {file}: {ex.Message}. A retry will be attempted.");
                                                transfer.RetryFile(file, fileSize);
                                                tryAgain = true;
                                            }
                                        }
                                        else
                                        {
                                            file         = thisFile;
                                            sourceStream = thisSourceStream;
                                            destStream   = thisDestStream;
                                            tryAgain     = false;
                                        }
                                    } while (tryAgain);
                                }

                                //Write the active buffer to the destination, update progress, and do a buffer swap.
                                try
                                {
                                    thisDestStream.Write(ActiveBuffer, 0, readLength);
                                }
                                catch (IOException ex)
                                {
                                    Utils.Logging.Logger.Error($"IOException writing to {thisFile}. Promting retry or cancel.", ex);

                                    string message = $"An exception occurred while writing {file}. Resolve the issue if possible and click OK to resume. Click cancel to abort the transfer.\r\n{ex.Message}";
                                    if (ex.Message.Contains("not enough space"))
                                    {
                                        message = $"Insufficient disk space, please free up some space and click Ok to continue. Otherwise, click cancel to abort.";
                                    }
                                    var result = System.Windows.MessageBox.Show(message, "IO Error", System.Windows.MessageBoxButton.OKCancel);
                                    if (result == System.Windows.MessageBoxResult.Cancel)
                                    {
                                        AbortTransfer(transfer);
                                    }
                                    else
                                    {
                                        SafeDisposeStream(thisSourceStream);
                                        SafeDisposeStream(thisDestStream);
                                        transfer.RetryFile(thisFile, readFailedMidStreamFileSize);
                                        return;
                                    }
                                }
                                thisUpdateMethod(thisTotalBytesRead);
                                BackBuffer = Interlocked.Exchange(ref ActiveBuffer, BackBuffer);
                                if (endOfFile == false)
                                {
                                    //If there is an abort request, dispose of our active streams and bail out. The fact that we dispose here and nowhere else is a little dirty...
                                    if (abortStream)
                                    {
                                        SafeDisposeStream(sourceStream);
                                        SafeDisposeStream(destStream);
                                        return;
                                    }
                                    else if (pauseStream)
                                    {
                                        transfer.PauseStreaming(thisFile, sourceStream, destStream, thisTotalBytesRead);
                                        return;
                                    }
                                }
                            }
                        }while ((readLength != 0) && (endOfFile == false));    //Break out once there is no more to read

                        if (readFailedMidStream)
                        {
                            SafeDisposeStream(thisSourceStream);
                            SafeDisposeStream(thisDestStream);
                            Utils.Logging.Logger.Error($"Exception reading from {thisFile}: {readFailedMidStreamException?.Message}. A retry will be attempted.");
                            transfer.RetryFile(thisFile, readFailedMidStreamFileSize);
                        }
                        else
                        {
                            try
                            {
                                thisFinishMethod?.Invoke(thisFile, thisSourceStream, thisDestStream); //Callback to the finish method,
                            }
                            catch (IOException ex)
                            {
                                Utils.Logging.Logger.Error($"Exception closing {thisFile}", ex);

                                string message = $"An exception occurred while writing {file}. Resolve the issue if possible and click OK to resume. Click cancel to abort the transfer.\r\n{ex.Message}";
                                if (ex.Message.Contains("not enough space"))
                                {
                                    message = $"Insufficient disk space, please free up some space and click Ok to continue. Otherwise, click cancel to abort.";
                                }
                                var result = System.Windows.MessageBox.Show(message, "IO Error", System.Windows.MessageBoxButton.OKCancel);
                                if (result == System.Windows.MessageBoxResult.Cancel)
                                {
                                    AbortTransfer(transfer);
                                    SafeDisposeStream(thisSourceStream);
                                    SafeDisposeStream(thisDestStream);
                                    return;
                                }
                                else
                                {
                                    SafeDisposeStream(thisSourceStream);
                                    SafeDisposeStream(thisDestStream);
                                    transfer.RetryFile(thisFile, readFailedMidStreamFileSize);
                                    return;
                                }
                            }
                        }
                    }
                }

                if (_IsPaused || (transfer != GetFirstTransfer()))
                {
                    transfer.Pause();
                    return;
                }
            } while (file != null); //If file is null, we've reached the end
            Steam.SteamBase.UiDispatcher.Invoke((Action)(() =>
            {
                lock (_transferLock)    //Acquire the transfer lock
                {
                    Transfers.Remove(transfer);
                }
            }));
            Steam.SteamBase.UiDispatcher.BeginInvoke((Action)(() => transfer.RunPostProcessing()));
        }