public override void TransferData(BackupDataSource destination, ProgressChange progressChange) { // Get the backup file from blob CloudBlockBlob zipBlob = this.container.GetBlockBlobReference(this.filename); BlobStream zipStream = zipBlob.OpenRead(); // Create a temp file for xml file CloudBlockBlob xmlBlob = container.GetBlockBlobReference(this.xmlFilename); BlobStream xmlStream = xmlBlob.OpenWrite(); // Unzip the backup file this.UnzipFile(zipStream, xmlStream); // Set delegate to class variable this.blockTransfer = destination.WriteData; this.progressChange = progressChange; // Open a stream to the xml file using (this.inStream = new StreamReader(xmlBlob.OpenRead())) { // The total number of backup operations is determined by the strem size this.TotalOperations = (int)this.inStream.BaseStream.Length; // Initialise detination writer destination.InitialiseWriting(this.GetTables(this.inStream)); // Split stream into batches & trigger delegate this.BatchStream(this.BlockTranferWithProgressUpdate, this.inStream.BaseStream); if (this.Cancelled) { // Exit the function if the user has cancelled return; } // Finalise detination writer destination.FinaliseWriting(); } // Delete the xml file xmlBlob.Delete(); }
/// <summary> /// Splits a stream of entites into matches according to Table Service rules. /// </summary> /// <param name="onBlockRead">The delegate to trigger per batch.</param> /// <param name="inStream">The input stream of entities.</param> protected void BatchStream(BlockTransfer onBlockRead, Stream inStream) { using (XmlTextReader reader = new XmlTextReader(inStream)) { MemoryStream outStream = new MemoryStream(); string batchTableName = null; string batchPartitionKey = null; string tableName; string partitionKey; Stream entityStream; int entityCount = 0; while (reader.Read()) { if (this.Cancelled) { // Exit the function if the user has cancelled return; } if (reader.Name == "entry" && reader.NodeType == XmlNodeType.Element) { // Get the entity entityStream = this.GetEntity(reader, out tableName, out partitionKey); entityCount++; if (batchTableName == null) { // First entity, so set the first batch table name & partition key batchTableName = tableName; batchPartitionKey = partitionKey; } // Can we add this entity to the current batch? if (entityCount > 100 || // A batch can have a max of 100 transactions (tableName != batchTableName) || // The batch must be within the same table (partitionKey != batchPartitionKey) || // The batch must be within the same partition ((entityStream.Length + outStream.Length) / 1024 > 3800)) // The max payload is 4mb, we make it less to allow for HTTP headers etc { // No, so send current batch to delegate outStream.Position = 0; onBlockRead(batchTableName, outStream); // Reset batch outStream = new MemoryStream(); entityCount = 1; batchTableName = tableName; batchPartitionKey = partitionKey; } // Add the entity to the current batch StreamUtil.CopyStream(entityStream, outStream); } } // Send any remaining entities to delegate outStream.Position = 0; onBlockRead(batchTableName, outStream); // Close the stream outStream.Close(); } }