Question

I'm developing a Windows Phone 7 project which uses a local SQLite database. The database is ~40MB uncompressed so I zipped it using maximum compression (Deflate) down to ~20MB. Here's my code (working).


    private void unzip_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
        IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
        IsolatedStorageFileStream file = new IsolatedStorageFileStream(filename, FileMode.Create, store);
        // TODO: switch from Deflate ~18.7MB to LZMA ~12.1MB (original ~41.5MB)
        StreamResourceInfo zipInfo = new StreamResourceInfo((Stream)e.Argument, null);
        StreamResourceInfo streamInfo = Application.GetResourceStream(zipInfo, new Uri(filename, UriKind.Relative));
        long total = streamInfo.Stream.Length;
        long done = 0;
        int size = 32768;
        byte[] data = new byte[size];
        while ((size = streamInfo.Stream.Read(data, 0, data.Length)) > 0)
        {
            file.Write(data, 0, size);
            done += size;
            int percentComplete = (int)(100 * ((float)done / (float)total));
            worker.ReportProgress(percentComplete);
        }
        file.Close();
    }

20MB is a good improvement but I noticed that a 7z archive using maximum compression (LZMA) achieves a file size of ~12MB. The zip file format supports LZMA content so I switched the Deflate compressed zip file for an LZMA compressed zip file and bang. I get NullReferenceException: Application.GetResourceStream(...) is returning null. Presumably that implementation doesn't handle LZMA content.

I tried another library but although it works fine for the Deflated zip, again it fails on the LZMA zip (NotSupportedException: Compression method not supported).


    using ICSharpCode.SharpZipLib.Zip;
    ...
    private void unzip_DoWork(object sender, DoWorkEventArgs e)
    {
    ...
        using (ZipInputStream zip = new ZipInputStream((Stream)e.Argument))
        {
            ZipEntry entry = zip.GetNextEntry(); // consume zip header (required)
            ....
        }
    }

I looked in NuGet and although there are a few C# libraries which claim to support LZMA decompression, they weren't compatible with my Windows Phone project (I think due to having been set up for .NET3 or .NET4 but not .NET3.5).

I thought about implementing a ICSharpCode.SharpZipLib.LZMA class using the LZMA SDK, but before I go reinventing any wheels I thought I should ask if anyone has successfully decompressed an LZMA zip on Windows Phone?

Any help much appreciated.

Was it helpful?

Solution

I continued looking for a Windows Phone implementation of LZMA decompression but no luck, so I made one based on the LZMA SDK version 9.22 beta. Posting the details here in case anyone finds it useful (no warranties, of course).

Project source can be found here: https://github.com/larryk78/SevenZip.Compression.LZMA.WindowsPhone/

The implementation offers three decompression scenarios (I didn't look at compression, yet):

  1. StreamDecoder wraps SevenZip.Compression.LZMA.Decoder (from the SDK) and offers stream-to-stream decompression.
  2. IsolatedStorageDecoder extends StreamDecoder to decompress from a stream or a file in IsolatedStorage, to a file in IsolatedStorage; i.e. stream-to-file and file-to-file, respectively.
  3. WebClient2IsolatedStorageDecoder extends IsolatedStorageDecoder to download a compressed file from a Uri, then decompress it to a file in IsolatedStorage.

N.B. StreamDecoder is implemented as a BackgroundWorker so decompression work is done asynchronously (doesn't block the UI thread) and progress can be hooked up to a ProgressBar.

Here's an example using WebClient2IsolatedStorageDecoder:

using SevenZip.Compression.LZMA.WindowsPhone;
using System.ComponentModel;

// <Button Content="Click Me!" Click="button_Click" />
private void button_Click(object sender, RoutedEventArgs e)
{
    WebClient2IsolatedStorageDecoder decoder = new WebClient2IsolatedStorageDecoder();
    decoder.ProgressChanged += new ProgressChangedEventHandler(decoder_ProgressChanged);
    decoder.RunWorkerCompleted += new RunWorkerCompletedEventHandler(decoder_RunWorkerCompleted);
    decoder.DecodeAsync(new Uri("http://example.com/compressed_file.lzma"), "decompressed_file.txt");
}

void decoder_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // <ProgressBar x:Name="Progress" />
    Progress.Value = e.ProgressPercentage;
}

void decoder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error)
    {
        MessageBox.Show("Couldn't download/decompress: " + e.Error.Message);
        return;
    }

    // ...do something with the decompressed data :)
}

Please let me know if you try it. There's nothing quite like Real User™ feedback :)

Cheers, Larry

P.S. this library doesn't handle *.zip files. It works on LZMA compressed files, i.e. *.lzma. You can make these using lzma.exe from the LZMA SDK (the binary is also in the github repo, linked above). I have had a chat with the maintainer of the ICSharpCode.SharpZipLib library about integrating LZMA into that and he's keen so let's see what happens there...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top