Xceed .NET Libraries Documentation
Retrieving a CompressedStream's remaining data

Welcome to Xceed .NET, .NET Standard and Xamarin Libraries! > Basic Concepts > Zip and streaming capabilities > Streaming compression > Retrieving a CompressedStream's remaining data

By default, the CompressedStream class closes the inner stream around which it is created once the compressed data has been decompressed. It is however possible to continue reading the inner stream's data once the compressed data has been decompressed.

Retrieving the remaining data

If the Transient property of the CompressedStream class is set to false, it will close its inner stream once the compressed data has been decompressed. In order to continue reading the inner stream's remaining data, the Transient property must be set to true. This will prevent the inner stream from being closed by the compressed stream. 

Because the CompressedStream class always read 32K at a time, if the size of the compressed data is not a multiple of 32, the CompressedStream class will read more than necessary. For example, if the total size of the compressed data is 42K, the first call to Read will read 32K and so will the second even if there was only 10K of compressed data left to read. Both the first and second calls will return the corresponding decompressed data. The third call to Read will return 0 meaning that the end of the compressed section of the stream has been reached. The extra data that was read will be discarded. 

To continue reading the stream once the compressed data has been decompressed, you could simply close the CompressedStream and continue reading the inner stream however you might not be positioned at the correct location in the case where too much data was read. In order to circumvent this behavior, you can use the GetRemainingStream method. The GetRemainingStream method will return a stream which starts immediately after the last byte of compressed data allowing you to continue reading the data.

 

 

In the case where more than one chunks of compressed data are appended to each other in the same stream, you can use the GetRemainingStream method to decompress the next chunk of compressed data (demonstrated in the example below ). 

Keep in mind that closing the stream returned by the GetRemainingStream method will not close the inner stream. You will need to call the inner stream's Close method yourself once you are finished with the returned stream.  

Demonstration

The following example demonstrates how to decompress appended chunks of compressed data contained within the same network stream.

VB.NET  

Imports Xceed.Compression
Imports Xceed.FileSystem
Imports System.IO
Imports System.Net.Sockets

' If your trial period has expired, you must purchase a registered license key,
' uncomment the appropriate line of code below, and insert your registered license key.
' For more information, jump to the How the 45-day trial works and the
' How to license the component topics.
' For Xceed Zip for .NET:
'Xceed.Compression.Licenser.LicenseKey = "ZINXX-XXXXX-XXXXX-XXXX"
' For Xceed Zip for .NET Compact Framework:
'Xceed.Compression.Licenser.LicenseKey = "ZICXX-XXXXX-XXXXX-XXXX"
' Note: Pathnames must be modified for code snippets to work under the .NET Compact Framework.

Dim client As New TcpClient( "MyServer", 42 )

' The returned stream does not close the connection when closed.
' It does not have ownership of the socket.
Dim stream As NetworkStream = client.GetStream()
UncompressNextChunk( stream, New DiskFolder( "d:\folder" ), 1 )

stream.Close()
client.Close()


Public Sub UncompressNextChunk( ByVal sourceStream As Stream, ByVal destFolder As DiskFolder, ByVal chunkCount As Integer )

' We want to decompress the next chunk from the source stream.
Dim compStream As New CompressedStream( sourceStream )

' But since we'll want to access the data following the compressed
' data in that source stream, we don't want the compressed stream to
' close it.
compStream.Transient = True

Dim destFile As DiskFile = CType( destFolder.GetFile( "Chunk" + chunkCount.ToString() + ".txt" ), DiskFile )

If Not destFile.Exists Then
destFile.Create()
End If

Dim endOfStream As Boolean = True

Dim destStream As Stream = destFile.OpenWrite( true )
Dim buffer( 32768 ) As Byte
Dim read As Integer = 0


Do
read = compStream.Read( buffer, 0, buffer.Length )

If read > 0 Then
endOfStream = False
destStream.Write( buffer, 0, read )
End If
Loop Until read = 0

destStream.Close()

' If we could not decompress anything at all, we consider this the
' real end of stream.
If endOfStream Then
return
End If

' Before closing the compressed stream, we want to grab a stream on
' the rest of the data. We cannot simply take sourceStream, since the
' CompressedStream may have read more than the compressed data. And to
' avoid having too many resources "alive", we delay calling ourselves
' recursively after closing the compressed stream, though it would be
' perfectly valid to make the call right here.
sourceStream = compStream.GetRemainingStream()
compStream.Close()

' We call ourselves recursively, with the stream returned by GetRemainingStream.
UncompressNextChunk( sourceStream, destFolder, chunkCount + 1 )
End Sub
C#  

using Xceed.Compression;
using Xceed.FileSystem;
using System.IO;
using System.Net.Sockets;

// If your trial period has expired, you must purchase a registered license key,
// uncomment the appropriate line of code below, and insert your registered license key.
// For more information, jump to the How the 45-day trial works and the
// How to license the component topics.
// For Xceed Zip for .NET:
//Xceed.Compression.Licenser.LicenseKey = "ZINXX-XXXXX-XXXXX-XXXX";
// For Xceed Zip for .NET Compact Framework:
//Xceed.Compression.Licenser.LicenseKey = "ZICXX-XXXXX-XXXXX-XXXX";
// Note: Pathnames must be modified for code snippets to work under the .NET Compact Framework.

using( TcpClient client = new TcpClient( "MyServer", 42 ) )
{
// The returned stream does not close the connection when closed.
// It does not have ownership of the socket.
using( NetworkStream stream = client.GetStream() )
{
UncompressNextChunk( stream, new DiskFolder( @"d:\folder" ), 1 );
}
}


public static void UncompressNextChunk( Stream sourceStream, DiskFolder destFolder, int chunkCount )
{
// We want to decompress the next chunk from the source stream.
using( CompressedStream compStream = new CompressedStream( sourceStream ) )
{
// But since we'll want to access the data following the compressed
// data in that source stream, we don't want the compressed stream to
// close it.
compStream.Transient = true;

DiskFile destFile = ( DiskFile )destFolder.GetFile( "Chunk" + chunkCount.ToString() + ".txt" );

if( !destFile.Exists )
destFile.Create();

bool endOfStream = true;

using( Stream destStream = destFile.OpenWrite( true ) )
{
byte[] buffer = new byte[ 32768 ];
int read = 0;

while( ( read = compStream.Read( buffer, 0, buffer.Length ) ) > 0 )
{
endOfStream = false;
destStream.Write( buffer, 0, read );
}
}

// If we could not decompress anything at all, we consider this the
// real end of stream.
if( endOfStream )
return;

// Before closing the compressed stream, we want to grab a stream on
// the rest of the data. We cannot simply take sourceStream, since the
// CompressedStream may have read more than the compressed data. And to
// avoid having too many resources "alive", we delay calling ourselves
// recursively after closing the compressed stream, though it would be
// perfectly valid to make the call right here.
sourceStream = compStream.GetRemainingStream();
}

// We call ourselves recursively, with the stream returned by GetRemainingStream.
UncompressNextChunk( sourceStream, destFolder, chunkCount + 1 );
}