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.
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):
StreamDecoder
wraps SevenZip.Compression.LZMA.Decoder
(from the SDK) and offers stream-to-stream decompression.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.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...