-
-
Save ststeiger/cb9750664952f775a341 to your computer and use it in GitHub Desktop.
// http://www.nullskull.com/a/768/7zip-lzma-inmemory-compression-with-c.aspx | |
// http://www.7-zip.org/sdk.html | |
namespace SevenZip.Compression.LZMA | |
{ | |
public static class SevenZipHelper | |
{ | |
static int dictionary = 1 << 23; | |
// static Int32 posStateBits = 2; | |
// static Int32 litContextBits = 3; // for normal files | |
// UInt32 litContextBits = 0; // for 32-bit data | |
// static Int32 litPosBits = 0; | |
// UInt32 litPosBits = 2; // for 32-bit data | |
// static Int32 algorithm = 2; | |
// static Int32 numFastBytes = 128; | |
static bool eos = false; | |
static CoderPropID[] propIDs = | |
{ | |
CoderPropID.DictionarySize, | |
CoderPropID.PosStateBits, | |
CoderPropID.LitContextBits, | |
CoderPropID.LitPosBits, | |
CoderPropID.Algorithm, | |
CoderPropID.NumFastBytes, | |
CoderPropID.MatchFinder, | |
CoderPropID.EndMarker | |
}; | |
// these are the default properties, keeping it simple for now: | |
static object[] properties = | |
{ | |
(System.Int32)(dictionary), | |
(System.Int32)(2), | |
(System.Int32)(3), | |
(System.Int32)(0), | |
(System.Int32)(2), | |
(System.Int32)(128), | |
"bt4", | |
eos | |
}; | |
public static byte[] Compress(byte[] inputBytes) | |
{ | |
byte[] retVal = null; | |
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder(); | |
encoder.SetCoderProperties(propIDs, properties); | |
using (System.IO.MemoryStream strmInStream = new System.IO.MemoryStream(inputBytes)) | |
{ | |
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream()) | |
{ | |
encoder.WriteCoderProperties(strmOutStream); | |
long fileSize = strmInStream.Length; | |
for (int i = 0; i < 8; i++) | |
strmOutStream.WriteByte((byte)(fileSize >> (8 * i))); | |
encoder.Code(strmInStream, strmOutStream, -1, -1, null); | |
retVal = strmOutStream.ToArray(); | |
} // End Using outStream | |
} // End Using inStream | |
return retVal; | |
} // End Function Compress | |
public static byte[] Compress(string inFileName) | |
{ | |
byte[] retVal = null; | |
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder(); | |
encoder.SetCoderProperties(propIDs, properties); | |
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read)) | |
{ | |
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream()) | |
{ | |
encoder.WriteCoderProperties(strmOutStream); | |
long fileSize = strmInStream.Length; | |
for (int i = 0; i < 8; i++) | |
strmOutStream.WriteByte((byte)(fileSize >> (8 * i))); | |
encoder.Code(strmInStream, strmOutStream, -1, -1, null); | |
retVal = strmOutStream.ToArray(); | |
} // End Using outStream | |
} // End Using inStream | |
return retVal; | |
} // End Function Compress | |
public static void Compress(string inFileName, string outFileName) | |
{ | |
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder(); | |
encoder.SetCoderProperties(propIDs, properties); | |
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read)) | |
{ | |
using (System.IO.Stream strmOutStream = new System.IO.FileStream(outFileName, System.IO.FileMode.Create)) | |
{ | |
encoder.WriteCoderProperties(strmOutStream); | |
long fileSize = strmInStream.Length; | |
for (int i = 0; i < 8; i++) | |
strmOutStream.WriteByte((byte)(fileSize >> (8 * i))); | |
encoder.Code(strmInStream, strmOutStream, -1, -1, null); | |
strmOutStream.Flush(); | |
strmOutStream.Close(); | |
} // End Using outStream | |
} // End Using inStream | |
} // End Function Compress | |
public static byte[] Decompress(string inFileName) | |
{ | |
byte[] retVal = null; | |
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); | |
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read)) | |
{ | |
strmInStream.Seek(0, 0); | |
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream()) | |
{ | |
byte[] properties2 = new byte[5]; | |
if (strmInStream.Read(properties2, 0, 5) != 5) | |
throw (new System.Exception("input .lzma is too short")); | |
long outSize = 0; | |
for (int i = 0; i < 8; i++) | |
{ | |
int v = strmInStream.ReadByte(); | |
if (v < 0) | |
throw (new System.Exception("Can't Read 1")); | |
outSize |= ((long)(byte)v) << (8 * i); | |
} //Next i | |
decoder.SetDecoderProperties(properties2); | |
long compressedSize = strmInStream.Length - strmInStream.Position; | |
decoder.Code(strmInStream, strmOutStream, compressedSize, outSize, null); | |
retVal = strmOutStream.ToArray(); | |
} // End Using newOutStream | |
} // End Using newInStream | |
return retVal; | |
} // End Function Decompress | |
public static void Decompress(string inFileName, string outFileName) | |
{ | |
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); | |
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read)) | |
{ | |
strmInStream.Seek(0, 0); | |
using (System.IO.Stream strmOutStream = new System.IO.FileStream(outFileName, System.IO.FileMode.Create)) | |
{ | |
byte[] properties2 = new byte[5]; | |
if (strmInStream.Read(properties2, 0, 5) != 5) | |
throw (new System.Exception("input .lzma is too short")); | |
long outSize = 0; | |
for (int i = 0; i < 8; i++) | |
{ | |
int v = strmInStream.ReadByte(); | |
if (v < 0) | |
throw (new System.Exception("Can't Read 1")); | |
outSize |= ((long)(byte)v) << (8 * i); | |
} // Next i | |
decoder.SetDecoderProperties(properties2); | |
long compressedSize = strmInStream.Length - strmInStream.Position; | |
decoder.Code(strmInStream, strmOutStream, compressedSize, outSize, null); | |
strmOutStream.Flush(); | |
strmOutStream.Close(); | |
} // End Using newOutStream | |
} // End Using newInStream | |
} // End Function Decompress | |
public static byte[] Decompress(byte[] inputBytes) | |
{ | |
byte[] retVal = null; | |
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); | |
using (System.IO.MemoryStream strmInStream = new System.IO.MemoryStream(inputBytes)) | |
{ | |
strmInStream.Seek(0, 0); | |
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream()) | |
{ | |
byte[] properties2 = new byte[5]; | |
if (strmInStream.Read(properties2, 0, 5) != 5) | |
throw (new System.Exception("input .lzma is too short")); | |
long outSize = 0; | |
for (int i = 0; i < 8; i++) | |
{ | |
int v = strmInStream.ReadByte(); | |
if (v < 0) | |
throw (new System.Exception("Can't Read 1")); | |
outSize |= ((long)(byte)v) << (8 * i); | |
} // Next i | |
decoder.SetDecoderProperties(properties2); | |
long compressedSize = strmInStream.Length - strmInStream.Position; | |
decoder.Code(strmInStream, strmOutStream, compressedSize, outSize, null); | |
retVal = strmOutStream.ToArray(); | |
} // End Using newOutStream | |
} // End Using newInStream | |
return retVal; | |
} // End Function Decompress | |
} // End Class SevenZipHelper | |
} // End Namespace SevenZip.Compression.LZMA |
I will just leave this in here for a little smoother API (Tested using latest LZMA 19.00 SDK)
Usage:
LZMA.CompressFile("input.txt", "output.txt.lzma" [, LzmaSpeed.Fastest, DictionarySize.VerySmall, Action<long, long> onProgress]);
LZMA.DecompressFile("input.txt", "output.txt.lzma" [, Action<long, long> onProgress])
LZMA.Compress(inStream, outStream [, LzmaSpeed.Fastest, DictionarySize.VerySmall, Action<long, long> onProgress]);
LZMA.Decompress(inStream, outStream [, Action<long, long> onProgress])
namespace Encoding.SevenZip
{
using System;
using System.IO.Compression;
using SevenZip;
using SevenZip.Compression.LZMA;
public enum LzmaSpeed : int
{
Fastest = 5,
VeryFast = 8,
Fast = 16,
Medium = 32,
Slow = 64,
VerySlow = 128,
}
public enum DictionarySize : int
{
///<summary>64 KiB</summary>
VerySmall = 1 << 16,
///<summary>1 MiB</summary>
Small = 1 << 20,
///<summary>4 MiB</summary>
Medium = 1 << 22,
///<summary>8 MiB</summary>
Large = 1 << 23,
///<summary>16 MiB</summary>
Larger = 1 << 24,
///<summary>64 MiB</summary>
VeryLarge = 1 << 26,
}
public static class LZMA
{
public static void CompressFile(string filename, string outfile, LzmaSpeed speed = LzmaSpeed.Fastest, DictionarySize dictionarySize = DictionarySize.VerySmall, Action<long, long>? onProgress = null)
{
using (var fs = File.OpenRead(filename))
using (var target = File.Create(outfile))
{
Compress(fs, target, speed, dictionarySize, onProgress);
}
}
public static void DecompressFile(string filename, string outfile, Action<long, long>? onProgress = null)
{
using (var fs = File.OpenRead(filename))
using (var target = File.Create(outfile))
{
Decompress(fs, target, onProgress);
}
}
public static void Compress(Stream input, Stream output, LzmaSpeed speed = LzmaSpeed.Fastest, DictionarySize dictionarySize = DictionarySize.VerySmall, Action<long, long>? onProgress = null)
{
int posStateBits = 2; // default: 2
int litContextBits = 3; // 3 for normal files, 0; for 32-bit data
int litPosBits = 0; // 0 for 64-bit data, 2 for 32-bit.
var numFastBytes = (int)speed;
string matchFinder = "BT4"; // default: BT4
bool endMarker = true;
CoderPropID[] propIDs =
{
CoderPropID.DictionarySize,
CoderPropID.PosStateBits, // (0 <= x <= 4).
CoderPropID.LitContextBits, // (0 <= x <= 8).
CoderPropID.LitPosBits, // (0 <= x <= 4).
CoderPropID.NumFastBytes,
CoderPropID.MatchFinder, // "BT2", "BT4".
CoderPropID.EndMarker
};
object[] properties =
{
(int)dictionarySize,
posStateBits,
(int)litContextBits,
(int)litPosBits,
numFastBytes,
matchFinder,
endMarker
};
var lzmaEncoder = new Encoder();
lzmaEncoder.SetCoderProperties(propIDs, properties);
lzmaEncoder.WriteCoderProperties(output);
var fileSize = input.Length;
for (int i = 0; i < 8; i++) output.WriteByte((byte)(fileSize >> (8 * i)));
ICodeProgress? prg = null;
if (onProgress != null)
{
prg = new DelegateCodeProgress(onProgress);
}
lzmaEncoder.Code(input, output, -1, -1, prg);
}
public static void Decompress(Stream input, Stream output, Action<long, long>? onProgress = null)
{
var decoder = new SevenZip.Compression.LZMA.Decoder();
byte[] properties = new byte[5];
if (input.Read(properties, 0, 5) != 5)
{
throw new Exception("input .lzma is too short");
}
decoder.SetDecoderProperties(properties);
long fileLength = 0;
for (int i = 0; i < 8; i++)
{
int v = input.ReadByte();
if (v < 0) throw new Exception("Can't Read 1");
fileLength |= ((long)(byte)v) << (8 * i);
}
ICodeProgress? prg = null;
if (onProgress != null)
{
prg = new DelegateCodeProgress(onProgress);
}
long compressedSize = input.Length - input.Position;
decoder.Code(input, output, compressedSize, fileLength, prg);
}
private class DelegateCodeProgress : ICodeProgress
{
private readonly Action<long, long> handler;
public DelegateCodeProgress(Action<long, long> handler) => this.handler = handler;
public void SetProgress(long inSize, long outSize) => handler(inSize, outSize);
}
}
}
I hope you read this, and kindly answer it.
Can you tell me how to compress / decompress multiple file at once ?
When I'm using your solution , it can compress only 1 file into 1 7z file.
Thank you.
LZMA.Decompress(inStream, outStream [, Action<long, long> onProgress])
How I can assing this to a ProgressBar?
LZMA.Decompress(inStream, outStream [, Action<long, long> onProgress])
How I can assing this to a ProgressBar?
Like this for example:
var fileStream = File.OpenRead("..."); // Get Filestream to compress
var zipStream = File.Create("..."); // Output file
var totalSize = fileStream.Length; // <- Get filesize once
// Using IProgress<T> for notifying
var prg = new Progress<(long processedBytes, long resultSize)>();
prg.ProgressChanged += (s, e) =>
{
// Update UI / Progressbar, etc.
// \/ this writes "Compressing: 1.52%" \/
Console.WriteLine($"Compressing: {e.processedBytes / totalSize:p}");
};
// you can do this in Task.Run(() => ...) - Progress<T> will raise the event on the creation Thread
// pass IProgress to progress lambda, so that you can call "Report" on it
IProgress<(long processedBytes, long resultSize)> iprg = prg;
LZMA.Compress(fileStream, zipStream, LzmaSpeed.Fastest, DictionarySize.Medium,
(cur, max) => iprg.Report((cur, max)));
@kirides Can you explain how to use this SDK to extract the 7z file into a destination folder with individual files?
@kirides Can you explain how to use this SDK to extract the 7z file into a destination folder with individual files?
tldr: Not with this kind of code
7z files are different from LZMA/2 compressed files.
7z files are archives (like .zip)
whereas LZMA/2 compressed files are more like "something.gz", or "dir.tar.xz"
Great Code!. I am trying to compress a folder. But i dont find any doc to do it. Writing filesize#1, file content#1, filesize#2, file content#2 does not seem to work.
Hi,
I really want to use your code.
Run it and I am getting an exception from the 'Code' method: "DataErrorException()"
if (rep0 == 0xFFFFFFFF)
break;
throw new DataErrorException();
Any idea why and how I can fix this?
I simply run the 'Decompress' method and gave it a 7z file name.