/// <summary> /// Reads a data block's stream and writes it to a given <paramref name="targetStream"/>. /// </summary> /// <param name="dataBlock">The data block that provides a chunk of data that should /// be written to the <paramref name="targetStream"/>.</param> /// <param name="targetStream">The target stream that receives the block's /// <see cref="StreamedDataBlock.Data"/>.</param> /// <param name="maxStreamSize">The maximum number of bytes that can be written to the destination /// stream. If the read stream exceeds this limit, a <see cref="DataBlockException"/> is thrown.</param> /// <exception cref="DataBlockException">If the data block's stream length exceeds the /// <paramref name="maxStreamSize"/> threshhold.</exception> public static void WriteTo(this StreamedDataBlock dataBlock, Stream targetStream, long maxStreamSize) { //use default byte sizes byte[] buffer = new byte[32768]; long totalBytesRead = 0; while (true) { int bytesRead = dataBlock.Data.Read(buffer, 0, buffer.Length); totalBytesRead += bytesRead; if (totalBytesRead > maxStreamSize) { string msg = "The length of the stream of data block number [{0}] for transfer [{1}] exceeds the size limit of [{2}] bytes."; msg = String.Format(msg, dataBlock.BlockNumber, dataBlock.TransferTokenId, maxStreamSize); throw new DataBlockException(msg); } if (bytesRead > 0) { targetStream.Write(buffer, 0, bytesRead); } else { targetStream.Flush(); break; } } }
/// <summary> /// Splits a stream into chunks using a <see cref="ChunkStream"/>, and wraps them into /// <see cref="StreamedDataBlock"/> instances that can be sent to an <see cref="IUploadTransferHandler"/>. /// This overload allows to resume a transfer and start with a given offset. /// <br/>This extension method also takes care of implicitly completing the upload by setting the /// <see cref="IDataBlock.IsLastBlock"/> property of the last block to true. /// </summary> /// <param name="sourceStream">The source stream that provides the data to be uploaded.</param> /// <param name="token">An upload token that defines the resource.</param> /// <param name="resourceLength">The total length of the submitted stream.</param> /// <param name="blockSize">The block size to be used. All blocks (except the last one) will have /// this size.</param> /// <param name="initialBlockNumber">The initial block number to be used.</param> /// <param name="offset">The offset of the first written block (0 to start at the beginning of the stream).</param> /// <param name="writerAction">An action that is being invoked for every created <see cref="StreamedDataBlock"/> /// instance.</param> /// <remarks>The position within the stream is only set according to the submitted /// <paramref name="offset"/> if the underlying <paramref name="sourceStream"/> supports seeking as indicated by /// its <see cref="Stream.CanSeek"/> property.</remarks> public static void WriteTo(this Stream sourceStream, UploadToken token, long resourceLength, int blockSize, long initialBlockNumber, long offset, Action <StreamedDataBlock> writerAction) { long remaining = resourceLength; long position = offset; long blockNumber = initialBlockNumber; while (remaining > 0) { //decorate the stream with a chunk stream that limits access to a block of data int chunkSize = (int)Math.Min(remaining, blockSize); ChunkStream cs = new ChunkStream(sourceStream, chunkSize, position, sourceStream.CanSeek); StreamedDataBlock dataBlock = new StreamedDataBlock { TransferTokenId = token.TransferId, BlockLength = chunkSize, BlockNumber = blockNumber, Data = cs, Offset = position }; //update position within stream and remaining bytes position += chunkSize; remaining -= chunkSize; blockNumber++; if (remaining == 0) { //implicitly complete the transfer by marking the last block dataBlock.IsLastBlock = true; } writerAction(dataBlock); } }
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; } } }
protected override void InitInternal() { base.InitInternal(); InitToken(); byte[] buffer = new byte[10000]; Array.Copy(SourceFileContents, buffer, 10000); BufferedBlock = new BufferedDataBlock { TransferTokenId = Token.TransferId, BlockLength = 10000, Data = buffer }; StreamedBlock = new StreamedDataBlock { TransferTokenId = Token.TransferId, BlockLength = 10000, Data = new ChunkStream(new MemoryStream(SourceFileContents), 10000, 0, false) }; }
private void WriteBlock(string transferId, long blockNumber, Stream input) { //get meta information from headers VfsHttpHeaders headerNames = VfsHttpHeaders.Default; HttpHeaderDictionary headers = Request.Headers; var blockLength = Convert.ToInt32(headers[headerNames.BlockLength]); var isLastBlock = headers.ContainsKey(headerNames.IsLastBlock) ? Convert.ToBoolean(headers[headerNames.IsLastBlock]) : false; var offset = Convert.ToInt64(headers[headerNames.BlockOffset]); StreamedDataBlock block = new StreamedDataBlock { TransferTokenId = transferId, BlockNumber = blockNumber, IsLastBlock = isLastBlock, BlockLength = blockLength, Offset = offset, Data = new NonSeekableStream(input) }; FileSystem.UploadTransfers.WriteBlockStreamed(block); }
protected override void InitInternal() { base.InitInternal(); InitToken(); //read first 10000 bytes byte[] buffer = GetBuffer(10000); BufferedBlock = new BufferedDataBlock { TransferTokenId = Token.TransferId, BlockLength = 10000, Data = buffer }; StreamedBlock = new StreamedDataBlock { TransferTokenId = Token.TransferId, BlockLength = 10000, Data = new ChunkStream(new MemoryStream(buffer), 10000, 0, false) }; }
/// <summary> /// /// </summary> public void WriteBlockStreamed(StreamedDataBlock block) { throw new NotImplementedException(); }
/// <summary> /// Splits a stream into chunks using a <see cref="ChunkStream"/>, and wraps them into /// <see cref="StreamedDataBlock"/> instances that can be sent to an <see cref="IUploadTransferHandler"/>. /// This overload allows to resume a transfer and start with a given offset. /// <br/>This extension method also takes care of implicitly completing the upload by setting the /// <see cref="IDataBlock.IsLastBlock"/> property of the last block to true. /// </summary> /// <param name="sourceStream">The source stream that provides the data to be uploaded.</param> /// <param name="token">An upload token that defines the resource.</param> /// <param name="resourceLength">The total length of the submitted stream.</param> /// <param name="blockSize">The block size to be used. All blocks (except the last one) will have /// this size.</param> /// <param name="initialBlockNumber">The initial block number to be used.</param> /// <param name="offset">The offset of the first written block (0 to start at the beginning of the stream).</param> /// <param name="writerAction">An action that is being invoked for every created <see cref="StreamedDataBlock"/> /// instance.</param> /// <remarks>The position within the stream is only set according to the submitted /// <paramref name="offset"/> if the underlying <paramref name="sourceStream"/> supports seeking as indicated by /// its <see cref="Stream.CanSeek"/> property.</remarks> public static void WriteTo(this Stream sourceStream, UploadToken token, long resourceLength, int blockSize, long initialBlockNumber, long offset, Action<StreamedDataBlock> writerAction) { long remaining = resourceLength; long position = offset; long blockNumber = initialBlockNumber; while(remaining > 0) { //decorate the stream with a chunk stream that limits access to a block of data int chunkSize = (int)Math.Min(remaining, blockSize); ChunkStream cs = new ChunkStream(sourceStream, chunkSize, position, sourceStream.CanSeek); StreamedDataBlock dataBlock = new StreamedDataBlock { TransferTokenId = token.TransferId, BlockLength = chunkSize, BlockNumber = blockNumber, Data = cs, Offset = position }; //update position within stream and remaining bytes position += chunkSize; remaining -= chunkSize; blockNumber++; if(remaining == 0) { //implicitly complete the transfer by marking the last block dataBlock.IsLastBlock = true; } writerAction(dataBlock); } }
/// <summary> /// Reads a data block's stream and writes it to a given <paramref name="targetStream"/>. /// </summary> /// <param name="dataBlock">The data block that provides a chunk of data that should /// be written to the <paramref name="targetStream"/>.</param> /// <param name="targetStream">The target stream that receives the block's /// <see cref="StreamedDataBlock.Data"/>.</param> public static void WriteTo(this StreamedDataBlock dataBlock, Stream targetStream) { targetStream.Position = dataBlock.Offset; dataBlock.Data.WriteTo(targetStream); }
/// <summary> /// Uploads a given data block that provides a chunk of data for an uploaded file as a stream. /// </summary> /// <param name="block">The block to be written.</param> /// <exception cref="DataBlockException">If the data block's contents cannot be stored, /// either because it's an invalid number, or because only sequential downloads /// are possible, and the block does not refer to the current download /// position. /// </exception> /// <exception cref="TransferStatusException">If the transfer has already expired.</exception> void IFSDataUploadService.WriteDataBlockStreamed(StreamedDataBlock block) { throw new NotImplementedException(); }