public void StartDownload() { if (Token == null) { Token = TransferService.RequestDownloadToken(ResourceId, false); } while (true) { IDataBlockInfo blockInfo; string transferId = Token.TransferId; long nextBlock = 0; if (Token.LastTransmittedBlockInfo != null) { nextBlock = Token.LastTransmittedBlockInfo.BlockNumber + 1; } if (UseStreamingTransfer) { StreamedDataBlock block = TransferService.ReadBlockStreamed(transferId, nextBlock); block.Data.WriteTo(OutputStream); blockInfo = block; } else { BufferedDataBlock block = TransferService.ReadBlock(transferId, nextBlock); OutputStream.Write(block.Data, 0, block.Data.Length); blockInfo = block; } //update the token, get independent file info without data or stream Token.LastTransmittedBlockInfo = DataBlockInfo.FromDataBlock(blockInfo); Token.LastBlockTransmissionTime = SystemTime.Now(); if (blockInfo.IsLastBlock) { //we're done - update token data Token.CompletionTime = Token.LastBlockTransmissionTime; TransferService.CompleteTransfer(transferId); Token.Status = TransferStatus.Completed; break; } } }
/// <summary> /// Orchestrates common block reading procedures for the <see cref="ReadBlock"/> and /// <see cref="ReadBlockStreamed"/> methods. /// </summary> protected virtual T OrchestrateBlockReading <T>(TTransfer transfer, long blockNumber, Func <TTransfer, long, DataBlockInfo, T> readerImplFunc) where T : IDataBlock { const FileSystemTask context = FileSystemTask.DataBlockDownloadRequest; if (blockNumber < 0) { string msg = "Invalid block number [{0}] requested - block numbers cannot be negative."; msg = String.Format(msg, blockNumber); throw new DataBlockException(msg); } if (blockNumber >= transfer.Token.TotalBlockCount) { string msg = "Invalid block number [{0}] requested - the total number of blocks is [{1}]."; msg = String.Format(msg, blockNumber, transfer.Token.TotalBlockCount); throw new DataBlockException(msg); } //make sure the file still exists if (!transfer.FileItem.Exists) { Auditor.AuditRequestedFileNotFound(transfer.FileItem, context); string msg = "Resource [{0}] of transfer [{1}] was not found."; msg = String.Format(msg, transfer.Token.ResourceName, transfer.TransferId); throw new VirtualResourceNotFoundException(msg) { IsAudited = true }; } lock (transfer.SyncRoot) { //make sure the transfer is active if (!transfer.Status.Is(TransferStatus.Starting, TransferStatus.Running, TransferStatus.Paused)) { string msg = String.Format("Transfer [{0}] is not active anymore - status is [{1}].", transfer.TransferId, transfer.Status); if (transfer.AbortReason.HasValue) { msg += String.Format(" Transfer abort reason: [{0}].", transfer.AbortReason); } Auditor.AuditDeniedOperation(context, AuditEvent.DownloadNoLongerActive, transfer.FileItem, msg); throw new TransferStatusException(msg) { IsAudited = true, EventId = (int)AuditEvent.DownloadNoLongerActive }; } //check whether we already transferred this block once DataBlockInfo transferredBlock = transfer.TryGetTransferredBlock(blockNumber); T dataBlock = readerImplFunc(transfer, blockNumber, transferredBlock); //update status transfer.Status = TransferStatus.Running; //store copy of the downloaded block that doesn't contain any data transfer.RegisterBlock(DataBlockInfo.FromDataBlock(dataBlock)); //audit download Auditor.AuditTransferOperation(context, AuditEvent.FileBlockDownloaded, transfer.TransferId, transfer.FileItem); return(dataBlock); } }
/// <summary> /// Handles locking, validation and stream preparation of a given transfer in order to read /// a given block of data. The actual reading (either into a buffer, or a returned /// stream) is being delegated via the <paramref name="dataReaderFunc"/>. /// </summary> /// <typeparam name="T">The type of <see cref="IDataBlockInfo"/> that is being returned.</typeparam> /// <param name="transferId">The transfer token ID.</param> /// <param name="blockNumber">The block number to be read.</param> /// <param name="dataReaderFunc">A function that receives the <see cref="DownloadTransfer"/> and /// the designated stream offset, and returns the required <see cref="IDataBlockInfo"/> that /// provides the block's data.</param> /// <returns>The <see cref="IDataBlockInfo"/> that is being created by the <paramref name="dataReaderFunc"/>.</returns> private T PrepareBlockReading <T>(string transferId, long blockNumber, Func <DownloadTransfer, long, T> dataReaderFunc) where T : IDataBlockInfo { DownloadTransfer transfer = GetCachedTransfer(transferId, true); DownloadToken token = transfer.Token; if (!File.Exists(transfer.File.FullName)) { string msg = "Resource [{0}] of transfer [{1}] was not found."; msg = String.Format(msg, transfer.Token.ResourceName, transferId); throw new VirtualResourceNotFoundException(msg); } lock (transfer.SyncRoot) { //make sure the transfer is active if (!transfer.Status.Is(TransferStatus.Starting, TransferStatus.Running, TransferStatus.Paused)) { string msg = String.Format("Transfer is not active anymore - status is [{0}].", transfer.Status); throw new TransferStatusException(msg); } long position = blockNumber * token.DownloadBlockSize.Value; //in case of an invalid position, throw error if (position > token.ResourceLength) { string msg = "Cannot deliver block {0} - invalid block number."; msg = String.Format(msg, blockNumber); throw new DataBlockException(msg); } if (transfer.Stream == null) { //open stream, share read access transfer.Stream = transfer.File.Open(FileMode.Open, FileAccess.Read, FileShare.Read); } //reposition stream if necessary if (position != transfer.Stream.Position) { transfer.Stream.Seek(position, SeekOrigin.Begin); } T dataBlock = dataReaderFunc(transfer, position); if (dataBlock.IsLastBlock) { //assume the last block will be processed successfully and //already close the stream (don't wait for the transfer to be //close by client) transfer.Stream.Close(); transfer.Stream.Dispose(); transfer.Stream = null; } //update status transfer.Token.Status = TransferStatus.Running; //maintain local (unshared) copy of the block info without the data transfer.Token.LastTransmittedBlockInfo = DataBlockInfo.FromDataBlock(dataBlock); return(dataBlock); } }