private Stream ReadFileImpl(string virtualFilePath) { //get a token - this creates a new transfer and validates the request DownloadToken token = RequestDownloadToken(virtualFilePath, false); //forward the token return(DownloadFileImpl(token.TransferId)); }
/// <summary> /// Requests a download token based on an older one. This allows to resume a paused /// or expired download if the service already discarded the token. /// </summary> /// <param name="oldToken">The previously used token.</param> /// <param name="includeFileHash">Whether a file hash for the /// requested resource should be calculated and assigned to the /// <see cref="DownloadToken.Md5FileHash"/> property of the returned /// <see cref="DownloadToken"/>. Can be used to verify file integrity, and also /// indicates whether the file was changed if the old token contained a file hash.</param> /// <returns>An updated token with a new expiration time, which reflects the /// status updates of the submitted token, including the <see cref="TransferToken.LastTransmittedBlockInfo"/></returns> public DownloadToken RenewToken(DownloadToken oldToken, bool includeFileHash) { //just create a new token - no adjustments needed with this service implementation var token = RequestDownloadToken(oldToken.ResourceIdentifier, includeFileHash); token.LastTransmittedBlockInfo = oldToken.LastTransmittedBlockInfo; token.LastBlockTransmissionTime = oldToken.LastBlockTransmissionTime; return(token); }
public void Init() { store = new InMemoryTransferStore<DownloadTransfer<FileItem>>(); transfers = new List<DownloadTransfer<FileItem>>(); for (int i = 0; i < 10; i++) { FileItem item = new FileItem {LocalFile = new FileInfo(i.ToString())}; var token = new DownloadToken {TransferId = i.ToString()}; transfers.Add(new DownloadTransfer<FileItem>(token, item)); } }
/// <summary> /// Requests a download token for a given resource. /// </summary> /// <param name="resourceIdentifier"></param> /// <param name="includeFileHash">Whether a file hash for the /// requested resource should be calculated and assigned to the /// <see cref="DownloadToken.Md5FileHash"/> property of the returned /// <see cref="DownloadToken"/>.</param> /// <returns></returns> public DownloadToken RequestDownloadToken(string resourceIdentifier, bool includeFileHash) { FileInfo fileInfo = FileResolveFunc(resourceIdentifier); if (!fileInfo.Exists) { string msg = String.Format("Resource [{0}] not found.", resourceIdentifier); throw new VirtualResourceNotFoundException(msg); } string transferId = Guid.NewGuid().ToString(); DownloadToken dt = new DownloadToken { TransferId = transferId, ResourceIdentifier = resourceIdentifier, CreationTime = SystemTime.Now(), ContentType = ContentUtil.ResolveContentType(fileInfo.Extension), DownloadBlockSize = 512 * 1024, //TODO configure block size and expiration ResourceName = fileInfo.Name, ResourceLength = fileInfo.Length, ExpirationTime = SystemTime.Now().AddHours(24), Status = TransferStatus.Starting }; //calculate number of blocks long count = dt.ResourceLength / dt.DownloadBlockSize.Value; if (dt.ResourceLength % dt.DownloadBlockSize != null) { count++; } dt.TotalBlockCount = count; if (includeFileHash) { dt.Md5FileHash = fileInfo.CalculateMd5Hash(); } var transfer = new DownloadTransfer(dt) { File = fileInfo }; Transfers.Add(transferId, transfer); return(dt); }
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> public DownloadManager(IDownloadTransferService transferService, string resourceId, DownloadToken token) { TransferService = transferService; ResourceId = resourceId; Token = token; }
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> protected OutboundTransferManager(DownloadToken token) { Token = token; }
/// <summary> /// Creates a transfer object for a given requested resource. /// </summary> /// <param name="submittedResourceFilePath">The resource identifier as submitted.</param> /// <param name="fileItem">Represents the requested file resource.</param> /// <param name="token">The token that is being issued for the transfer.</param> /// <param name="claims">The access rights for the resource.</param> /// <param name="lockGuard">File locks, if necessary. Can be a null reference /// if no locking takes place.</param> /// <param name="expirationJob">A scheduled job that invokes the /// <see cref="TransferHandlerBase{TFile,TToken,TTransfer}.OnTransferExpiration"/> /// method once the transfer expires. May be null if the token does not expire.</param> /// <returns>A transfer object which encapsulates the information required to perform /// the transfer.</returns> protected abstract TTransfer CreateTransfer(string submittedResourceFilePath, TFile fileItem, DownloadToken token, FileClaims claims, ResourceLockGuard lockGuard, Job <DownloadToken> expirationJob);
private TTransfer InitTransferImpl(string submittedResourceFilePath, int?clientMaxBlockSize, bool includeFileHash) { const FileSystemTask context = FileSystemTask.DownloadTokenRequest; TFile fileItem = CreateFileItemImpl(submittedResourceFilePath, false, context); if (!fileItem.Exists) { Auditor.AuditRequestedFileNotFound(fileItem, context); string msg = String.Format("Resource [{0}] not found.", submittedResourceFilePath); throw new VirtualResourceNotFoundException(msg) { IsAudited = true }; } //get authorization FileClaims claims = GetFileClaims(fileItem); if (!claims.AllowReadData) { Auditor.AuditDeniedOperation(context, AuditEvent.FileDataDownloadDenied, fileItem); string msg = "Read request for file [{0}] was denied - you are not authorized to read the resource."; msg = String.Format(msg, submittedResourceFilePath); throw new ResourceAccessException(msg) { IsAudited = true }; } //try to get lock ResourceLockGuard readLock = LockResourceForDownload(fileItem, claims); if (readLock != null && !readLock.IsLockEnabled) { Auditor.AuditDeniedOperation(context, AuditEvent.FileReadLockDenied, fileItem); string msg = "The requested file [{0}] is currently locked and thus cannot be accessed."; msg = String.Format(msg, submittedResourceFilePath); throw new ResourceLockedException(msg) { IsAudited = true }; } //create download token DownloadToken token = CreateDownloadToken(submittedResourceFilePath, fileItem, clientMaxBlockSize, includeFileHash); //create expiration job if we have an expiration time Job <DownloadToken> job = null; if (token.ExpirationTime.HasValue) { job = ScheduleExpiration(token); } //create transfer instance TTransfer transfer = CreateTransfer(submittedResourceFilePath, fileItem, token, claims, readLock, job); //cache transfer TransferStore.AddTransfer(transfer); //audit issued token Auditor.AuditResourceOperation(context, AuditEvent.DownloadTokenIssued, fileItem); return(transfer); }
/// <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); } }
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> public DownloadTransfer(DownloadToken token) { Token = token; SyncRoot = new object(); }