/// <summary> /// Gets information about all blocks that have been transferred so far. /// This information can be used to resume a transfer that spawns several /// sessions on the client side.<br/> /// In case of retransmissions of blocks, this method returns only the /// <see cref="DataBlockInfo"/> instance per block number that corresponds /// to the most recent transfer. /// </summary> /// <param name="transferId">Identifies the transfer and resource.</param> /// <returns>All transferred blocks (without data).</returns> /// <exception cref="UnknownTransferException">In case no such transfer /// is currently maintained.</exception> /// <remarks>Implementing classes might decide to discard cached block information /// once a transfer was completed, and throw exceptions or just return an empty list.</remarks> public IEnumerable <DataBlockInfo> GetTransferredBlocks(string transferId) { var context = IsUploadService ? FileSystemTask.UploadedBlockInfosRequest : FileSystemTask.DownloadedBlockInfosRequest; TTransfer transfer = GetCachedTransfer(transferId, true, context); var blocks = transfer.TransferredBlocks; Auditor.AuditTransferOperation(context, AuditEvent.TransferredBlockInfoRequest, transferId, transfer.FileItem); return(blocks); }
private Stream DownloadFileImpl(string transferId) { const FileSystemTask context = FileSystemTask.StreamedFileDownloadRequest; //get the cached transfer TTransfer transfer = GetCachedTransfer(transferId, true, context); if (transfer.Token.TotalBlockCount == 0) { //if we don't have any blocks, return a null stream Auditor.AuditTransferOperation(context, AuditEvent.FileDataDownloaded, transfer.TransferId, transfer.FileItem); return(Stream.Null); } //we don't expose the underlying stream directly, but rather use blocks again, //which disconnects the exposed stream from the underlying resource and allows for simple //aborting long resourceLength = transfer.FileItem.ResourceInfo.Length; // ReSharper disable UseObjectOrCollectionInitializer var stream = new StreamedBlockInputStream(blockNumber => ReadBlockStreamed(transferId, blockNumber), resourceLength); // ReSharper restore UseObjectOrCollectionInitializer //assign the stream the transfer object in order so it can query the status during reading stream.Transfer = transfer; //reading the last block closes the transfer automatically.. transfer.AutoCloseAfterLastBlockDelivery = true; //...and so does disposing the stream at any time var closingStream = new ClosingActionStream(stream, () => CompleteTransfer(transferId)); Auditor.AuditTransferOperation(context, AuditEvent.FileDataDownloaded, transfer.TransferId, transfer.FileItem); return(closingStream); }
/// <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); } }