/// <summary> /// Handles the creation of an actual data block based on the underlying resource. /// </summary> /// <param name="transfer">The processed transfer.</param> /// <param name="blockNumber">The number of the downloaded block.</param> /// <param name="previouslyTransferredBlock">If this data block was already transferred, this parameter /// contains the information about the block. Can be used in order to ensure proper retransmission in case /// of variable block sizes.</param> /// <returns>A data block which exposes the data as a resource-friendly stream /// (<see cref="BufferedDataBlock.Data"/>).</returns> protected override StreamedDataBlock CreateStreamedDataBlockImpl(LocalDownloadTransfer transfer, long blockNumber, DataBlockInfo previouslyTransferredBlock) { //this func creates the returned DataBlock by reading a chunk of data //from the underlying stream. Func <long, StreamedDataBlock> func = position => { DownloadToken token = transfer.Token; //check if we can use the max block size long streamLength = transfer.Stream.Length; int blockLength = (int)Math.Min(token.DownloadBlockSize, streamLength - position); if (blockLength < 0) { blockLength = 0; } ChunkStream stream = new ChunkStream(transfer.Stream, blockLength, position, false); return(new StreamedDataBlock { TransferTokenId = transfer.TransferId, BlockNumber = blockNumber, BlockLength = blockLength, Offset = position, Data = stream, IsLastBlock = blockNumber == token.TotalBlockCount - 1 }); }; return(PrepareAndRunBlockReading(transfer, blockNumber, func)); }
/// <summary> /// 删除录播任务 /// </summary> public void DeleteRecording(DownloadToken token) { var key = token.LinkedSchedule.Key; RecordScheduleManager.DeleteSchedule(token.LinkedSchedule); _TaskList.Remove(key); TaskList.Remove(token); }
public PartialViewResult ConfigExchange() { ViewData["ConfigLink"] = new DownloadToken { ContentType = "application/json", DownloadName = options.DownloadName, DownloadReason = options.DownloadReason, FileDownload = true, FileIdentifier = options.FileIdentifier, HandlerModuleName = options.UploadModuleName }.CompressToken(); return(PartialView(options)); }
private void BuildDownloadToken(TutorialStream model, TutorialStreamViewModel vm) { var token = new DownloadToken { FileDownload = true, ContentType = vm.ContentType, DownloadName = $"Video{model.TutorialStreamId}.{model.ContentType.Substring(model.ContentType.IndexOf("/") + 1)}", DownloadReason = "DownloadTutorial", FileIdentifier = $"##VID#{vm.VideoTutorialId}#{vm.TutorialStreamId}", HandlerModuleName = VideoFileHandler }.CompressToken(); vm.DownloadToken = token; }
/// <summary> /// Creates a token for the currently processed resource. /// </summary> /// <param name="submittedResourceFilePath">The identifier (file path) /// for the requested resource, as submitted by the requesting party.</param> /// <param name="fileItem">Represents the requested file.</param> /// <param name="clientMaxBlockSize">The maximum size of a downloadable block, as requested by /// the client. It is up to the implementing method to correct this value (e.g. in order /// to comply to the <see cref="TransferHandlerBase{TFile,TToken,TTransfer}.MaxBlockSize"/> /// of the service itself.</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>A token for the request.</returns> protected override DownloadToken CreateDownloadToken(string submittedResourceFilePath, ZipFileItem fileItem, int?clientMaxBlockSize, bool includeFileHash) { var fileInfo = fileItem.ResourceInfo; string transferId = Guid.NewGuid().ToString(); //try to comply to the requested block size, if specified. Otherwise //apply the default int blockSize = clientMaxBlockSize.HasValue ? Math.Min(clientMaxBlockSize.Value, MaxBlockSize ?? int.MaxValue) : Configuration.FileSystemConfiguration.DefaultDownloadBlockSize; var token = new DownloadToken { TransferId = transferId, ResourceIdentifier = submittedResourceFilePath, CreationTime = SystemTime.Now(), ContentType = fileInfo.ContentType, ResourceName = fileInfo.Name, ResourceLength = fileInfo.Length, DownloadBlockSize = blockSize }; TimeSpan?expiration = Configuration.FileSystemConfiguration.DownloadTokenExpirationTime; if (expiration.HasValue) { token.ExpirationTime = SystemTime.Now().Add(expiration.Value); } //calculate number of blocks long blockCount = token.ResourceLength / blockSize; if ((token.ResourceLength % token.DownloadBlockSize) != 0) { blockCount++; } token.TotalBlockCount = blockCount; if (includeFileHash) { using (var reader = fileItem.Node.FileEntry.OpenReader()) { token.Md5FileHash = reader.CalculateMd5Hash(); } } 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> /// Handles the creation of an actual data block based on the underlying resource. /// </summary> /// <param name="transfer">The processed transfer.</param> /// <param name="blockNumber">The number of the downloaded block.</param> /// <param name="previouslyTransferredBlock">If this data block was already transferred, this parameter /// contains the information about the block. Can be used in order to ensure proper retransmission in case /// of variable block sizes.</param> /// <returns>A data block which contains the data as an in-memory buffer /// (<see cref="BufferedDataBlock.Data"/>).</returns> protected override BufferedDataBlock CreateBufferedDataBlockImpl(LocalDownloadTransfer transfer, long blockNumber, DataBlockInfo previouslyTransferredBlock) { //this func creates the returned DataBlock by reading a chunk of data //from the underlying stream. Func <long, BufferedDataBlock> func = position => { DownloadToken token = transfer.Token; //read data byte[] data = new byte[token.DownloadBlockSize]; int read = transfer.Stream.Read(data, 0, data.Length); if (read < token.DownloadBlockSize) { Array.Resize(ref data, read); } return(new BufferedDataBlock { TransferTokenId = transfer.TransferId, BlockNumber = blockNumber, BlockLength = data.Length, Offset = position, Data = data, IsLastBlock = blockNumber == token.TotalBlockCount - 1 }); }; var dataBlock = PrepareAndRunBlockReading(transfer, blockNumber, func); if (dataBlock.IsLastBlock) { //assume the last block will be processed successfully and //already close the stream (don't wait for the transfer to be //closed by client). This would still reopen the stream if //necessary CloseStream(transfer); } return(dataBlock); }
/// <summary> /// 在前台创建录播任务 /// </summary> public DownloadToken StartRecording(Channel channel, SourceRecord source, DateTimeOffset startTime, TimeSpan span) { // 注册相关信息 string identifer = channel.UniqueId + source.StationName + startTime.ToString("yyMMddHHmmss"); var schedule = RecordScheduleManager.CreateSchedule(identifer, source.Source.AbsoluteUri, startTime, span); var token = new DownloadToken() { Channel = channel, Source = source.StationName, LinkedSchedule = schedule }; _TaskList.Add(schedule.Key, token); TaskList.Add(token); // 开始任务 var diff = startTime.Subtract(DateTimeOffset.Now); if (diff > TimeSpan.FromMinutes(15)) { RecordScheduleManager.LaunchSchedule(schedule); new MessageDialog("由于系统原因,后台定时任务的精度为15分钟,为保证录下您指定的时间段,将提前15分钟开始录制,请预留足够的硬盘空间", "提示").ShowAsync(); } else { if (diff <= TimeSpan.Zero) { RecordScheduleManager.LaunchRecording(schedule); } else { // TODO: 由于ThreadPoolTimer会受到程序进入后台的影响,因此可以考虑在这里改用后台任务强行等待 new MessageDialog("由于系统不支持15分钟内的后台定时任务,请您在录制开始前将本程序保持开启并避免最小化!开始后即可关闭程序", "提示").ShowAsync(); ThreadPoolTimer.CreateTimer((timer) => RecordScheduleManager.LaunchRecording(schedule), diff); } } return(token); }
public void Refreshing_Token_After_Multiple_Read_Should_Still_Indicate_The_Last_Block_Number() { for (int i = 0; i < 10; i++) { DownloadService.ReadBlock(Token.TransferId, i); } DataBlockInfo lastBlock = null; //redownload a few of these blocks for (int i = 0; i < 10; i++) { if (i % 2 == 0) { lastBlock = DownloadService.ReadBlock(Token.TransferId, i); } } DownloadToken copy = DownloadService.RenewToken(Token, false); Assert.AreEqual(lastBlock.BlockNumber, copy.LastTransmittedBlockInfo.BlockNumber); }
/// <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 override LocalDownloadTransfer CreateTransfer(string submittedResourceFilePath, FileItem fileItem, DownloadToken token, FileClaims claims, ResourceLockGuard lockGuard, Job <DownloadToken> expirationJob) { var transfer = new LocalDownloadTransfer(token, fileItem) { Status = TransferStatus.Starting }; if (expirationJob != null) { transfer.ExpirationNotificationJob = expirationJob; } if (lockGuard != null) { transfer.ResourceLock = lockGuard; } transfer.Owner = Config.Provider.Security.GetIdentity(); return(transfer); }
private RecordService() { // 如果没有恢复TaskList话则手动恢复 if (_TaskList.Count == 0) { Utils.Async.InvokeAndWait(async() => await _TaskList.Restore()); } // 同步前后台的列表 foreach (var schedule in RecordScheduleManager.GetSchedules()) { var key = schedule.Key; if (_TaskList.ContainsKey(key)) { _TaskList[key].LinkedSchedule = schedule; } else { var token = new DownloadToken() { Channel = Channel.GetChannel("unknown", "未知频道"), Source = "未知来源", LinkedSchedule = schedule }; _TaskList.Add(schedule.Key, token); } } foreach (var item in _TaskList.ToList()) // 利用ToList方法创建副本 { if (item.Value.LinkedSchedule == null) { _TaskList.Remove(item.Key); } } // 初始化前台列表并添加事件监听 TaskList = new ObservableCollection <DownloadToken>(_TaskList.Values); }
/// <summary> /// 终止后台录播任务 /// </summary> public void TerminateRecording(DownloadToken token) => RecordScheduleManager.TerminateSchedule(token.LinkedSchedule);
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> public LocalDownloadTransfer(DownloadToken token, FileItem fileItem) : base(token, fileItem) { }
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> public DownloadTransfer(DownloadToken token, TFile fileItem) : base(token, fileItem) { }
/// <summary> /// Initializes a new instance of the <see cref="T:System.Object"/> class. /// </summary> public ZipDownloadTransfer(DownloadToken token, ZipFileItem fileItem) : base(token, fileItem) { }