将Brotli编译成DLL .NET可以参考
Compile Brotli into a DLL .NET can reference
所以我想利用 Brotli,但我不熟悉 Python 和 C++..
我知道有人将其编译成 Windows.exe。但是我如何将它包装到 DLL 或 .NET 应用程序可以引用的东西中呢?我知道有 IronPython,我是否只是将所有源文件引入 IronPython 项目并编写一个调用 Brotli API 并公开它们的 .NET 适配器?但实际上,我什至不确定 Brotli API 是 Python 还是 C++..
查看 tools/bro.cc
,看起来 "entry" 方法在 encode.c
和 decode.c
中定义为 BrotliCompress()
、BrotliDecompressBuffer()
, BrotliDecompressStream()
方法。所以我想可以从 C++ 类.
编译一个 DLL
我将展示一种通过从 .NET 代码调用 python 本机库来实现此目的的方法。你需要什么:
- 您需要安装 python 2.7(希望这是显而易见的)
您需要从源代码编译 brotli。希望这很容易。首先安装Microsoft Visual C++ compiler for Python 2.7。然后通过 git clone https://github.com/google/brotli.git
克隆 brotli 存储库并使用 python setup.py build_ext
编译。完成后,在 build\lib.win32-2.7
目录中您将找到 brotli.pyd
文件。这是 python c++ 模块 - 我们稍后会用到它。
您需要下载 pythonnet 二进制文件或从源代码编译它。我们在这里使用 pythonnet 而不是 Iron Python 的原因是因为 Iron Python 不支持原生 (C\C++) python 模块,这就是我们在这里所需要的。因此,要从源代码编译,通过 git clone https://github.com/pythonnet/pythonnet.git
克隆然后通过 python setup.py build
编译。结果你会得到 Python.Runtime.dll(在 build\lib.win32-2.7
目录),这就是我们需要的。
当你准备好所有这些后,创建控制台项目,引用 Python.Runtime.dll 然后:
public static void Main()
{
PythonEngine.Initialize();
var gs = PythonEngine.AcquireLock();
try {
// import brotli module
dynamic brotli = PythonEngine.ImportModule(@"brotli");
// this is a string we will compress
string original = "XXXXXXXXXXYYYYYYYYYY";
// compress and interpret as byte array. This array you can save to file for example
var compressed = (byte[]) brotli.compress(original);
// little trick to pass byte array as python string
dynamic base64Encoded = new PyString(Convert.ToBase64String(compressed));
// decompress and interpret as string
var decompressed = (string) brotli.decompress(base64Encoded.decode("base64"));
// works
Debug.Assert(decompressed == original);
}
finally {
PythonEngine.ReleaseLock(gs);
PythonEngine.Shutdown();
}
Console.ReadKey();
}
然后构建它,然后将你得到的brotli.pyc放在与你的.exe文件相同的目录中。在所有这些操作之后,您将能够从 .NET 代码中压缩和解压缩,如上所示。
为了避免对 Python 的需要,我在这里 https://github.com/smourier/brotli 分支了原始的 brotli 源代码并创建了它的 Windows DLL 版本,您可以将其与 .NET 一起使用。
我添加了一个目录,其中包含一个 "WinBrotli" Visual Studio 2015 解决方案和两个项目:
- WinBrotli:一个 Windows DLL(x86 和 x64),包含原始未更改的 C/C++ brotli 代码。
- Brotli:用 C# 编写的 Windows 控制台应用程序(任何 Cpu),其中包含 P/Invoke WinBrotli 的互操作代码。
要重用 Winbrotli DLL,只需复制 WinBrotli.x64.dll 和 WinBrotli.x86.dll(您可以在 WinBrotli/binaries 文件夹)放在您的 .NET 应用程序旁边,并将 BrotliCompression.cs 文件合并到您的 C# 项目中(或者将其移植到 VB 或其他语言,如果 C# 是不是你最喜欢的语言)。互操作代码将自动选择与当前进程的位数(X86 或 X64)相对应的正确 DLL。
完成后,使用起来相当简单(输入和输出可以是文件路径或标准 .NET 流):
// compress
BrotliCompression.Compress(input, output);
// decompress
BrotliCompression.Decompress(input, output);
为了创建 WinBrotli,这是我所做的(对于其他想要使用其他 Visual Studio 版本的人)
- 创建了标准 DLL 项目,删除了预编译头文件
- 包括所有编码器和解码器原始 brotli C/C++ 文件(其中未更改任何内容,因此我们可以在需要时更新原始文件)
- 配置项目以去除对 MSVCRT 的依赖(因此我们不需要部署其他 DLL)
- 禁用 4146 警告(否则我们无法编译)
- 添加了一个非常标准的 dllmain.cpp 文件,没有什么特别之处
- 添加了一个 WinBrotli.cpp 文件,将 brotli 压缩和解压缩代码暴露给外部 Windows 世界(具有非常薄的适配层,因此在 .NET 中更容易互操作)
- 添加了导出 4 个函数的 WinBrotli.def 文件
您可以使用 Brotli.NET,它提供完整的流支持。
将流压缩为 brotli 数据:
public Byte[] Encode(Byte[] input)
{
Byte[] output = null;
using (System.IO.MemoryStream msInput = new System.IO.MemoryStream(input))
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
using (BrotliStream bs = new BrotliStream(msOutput, System.IO.Compression.CompressionMode.Compress))
{
bs.SetQuality(11);
bs.SetWindow(22);
msInput.CopyTo(bs);
bs.Close();
output = msOutput.ToArray();
return output;
}
}
要解压缩 brotli 流:
public Byte[] Decode(Byte[] input)
{
using (System.IO.MemoryStream msInput = new System.IO.MemoryStream(input))
using (BrotliStream bs = new BrotliStream(msInput, System.IO.Compression.CompressionMode.Decompress))
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
{
bs.CopyTo(msOutput);
msOutput.Seek(0, System.IO.SeekOrigin.Begin);
output = msOutput.ToArray();
return output;
}
}
为了在网络应用中支持动态压缩,在Global.asax.cs中添加这样的代码:
protected void Application_PostAcquireRequestState(object sender, EventArgs e)
{
var app = Context.ApplicationInstance;
String acceptEncodings = app.Request.Headers.Get("Accept-Encoding");
if (!String.IsNullOrEmpty(acceptEncodings))
{
System.IO.Stream baseStream = app.Response.Filter;
acceptEncodings = acceptEncodings.ToLower();
if (acceptEncodings.Contains("br") || acceptEncodings.Contains("brotli"))
{
app.Response.Filter = new Brotli.BrotliStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "br");
}
else
if (acceptEncodings.Contains("deflate"))
{
app.Response.Filter = new System.IO.Compression.DeflateStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "deflate");
}
else if (acceptEncodings.Contains("gzip"))
{
app.Response.Filter = new System.IO.Compression.GZipStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
}
}
所以我想利用 Brotli,但我不熟悉 Python 和 C++..
我知道有人将其编译成 Windows.exe。但是我如何将它包装到 DLL 或 .NET 应用程序可以引用的东西中呢?我知道有 IronPython,我是否只是将所有源文件引入 IronPython 项目并编写一个调用 Brotli API 并公开它们的 .NET 适配器?但实际上,我什至不确定 Brotli API 是 Python 还是 C++..
查看 tools/bro.cc
,看起来 "entry" 方法在 encode.c
和 decode.c
中定义为 BrotliCompress()
、BrotliDecompressBuffer()
, BrotliDecompressStream()
方法。所以我想可以从 C++ 类.
我将展示一种通过从 .NET 代码调用 python 本机库来实现此目的的方法。你需要什么:
- 您需要安装 python 2.7(希望这是显而易见的)
您需要从源代码编译 brotli。希望这很容易。首先安装Microsoft Visual C++ compiler for Python 2.7。然后通过
git clone https://github.com/google/brotli.git
克隆 brotli 存储库并使用python setup.py build_ext
编译。完成后,在build\lib.win32-2.7
目录中您将找到brotli.pyd
文件。这是 python c++ 模块 - 我们稍后会用到它。您需要下载 pythonnet 二进制文件或从源代码编译它。我们在这里使用 pythonnet 而不是 Iron Python 的原因是因为 Iron Python 不支持原生 (C\C++) python 模块,这就是我们在这里所需要的。因此,要从源代码编译,通过
git clone https://github.com/pythonnet/pythonnet.git
克隆然后通过python setup.py build
编译。结果你会得到 Python.Runtime.dll(在build\lib.win32-2.7
目录),这就是我们需要的。
当你准备好所有这些后,创建控制台项目,引用 Python.Runtime.dll 然后:
public static void Main()
{
PythonEngine.Initialize();
var gs = PythonEngine.AcquireLock();
try {
// import brotli module
dynamic brotli = PythonEngine.ImportModule(@"brotli");
// this is a string we will compress
string original = "XXXXXXXXXXYYYYYYYYYY";
// compress and interpret as byte array. This array you can save to file for example
var compressed = (byte[]) brotli.compress(original);
// little trick to pass byte array as python string
dynamic base64Encoded = new PyString(Convert.ToBase64String(compressed));
// decompress and interpret as string
var decompressed = (string) brotli.decompress(base64Encoded.decode("base64"));
// works
Debug.Assert(decompressed == original);
}
finally {
PythonEngine.ReleaseLock(gs);
PythonEngine.Shutdown();
}
Console.ReadKey();
}
然后构建它,然后将你得到的brotli.pyc放在与你的.exe文件相同的目录中。在所有这些操作之后,您将能够从 .NET 代码中压缩和解压缩,如上所示。
为了避免对 Python 的需要,我在这里 https://github.com/smourier/brotli 分支了原始的 brotli 源代码并创建了它的 Windows DLL 版本,您可以将其与 .NET 一起使用。 我添加了一个目录,其中包含一个 "WinBrotli" Visual Studio 2015 解决方案和两个项目:
- WinBrotli:一个 Windows DLL(x86 和 x64),包含原始未更改的 C/C++ brotli 代码。
- Brotli:用 C# 编写的 Windows 控制台应用程序(任何 Cpu),其中包含 P/Invoke WinBrotli 的互操作代码。
要重用 Winbrotli DLL,只需复制 WinBrotli.x64.dll 和 WinBrotli.x86.dll(您可以在 WinBrotli/binaries 文件夹)放在您的 .NET 应用程序旁边,并将 BrotliCompression.cs 文件合并到您的 C# 项目中(或者将其移植到 VB 或其他语言,如果 C# 是不是你最喜欢的语言)。互操作代码将自动选择与当前进程的位数(X86 或 X64)相对应的正确 DLL。
完成后,使用起来相当简单(输入和输出可以是文件路径或标准 .NET 流):
// compress
BrotliCompression.Compress(input, output);
// decompress
BrotliCompression.Decompress(input, output);
为了创建 WinBrotli,这是我所做的(对于其他想要使用其他 Visual Studio 版本的人)
- 创建了标准 DLL 项目,删除了预编译头文件
- 包括所有编码器和解码器原始 brotli C/C++ 文件(其中未更改任何内容,因此我们可以在需要时更新原始文件)
- 配置项目以去除对 MSVCRT 的依赖(因此我们不需要部署其他 DLL)
- 禁用 4146 警告(否则我们无法编译)
- 添加了一个非常标准的 dllmain.cpp 文件,没有什么特别之处
- 添加了一个 WinBrotli.cpp 文件,将 brotli 压缩和解压缩代码暴露给外部 Windows 世界(具有非常薄的适配层,因此在 .NET 中更容易互操作)
- 添加了导出 4 个函数的 WinBrotli.def 文件
您可以使用 Brotli.NET,它提供完整的流支持。
将流压缩为 brotli 数据:
public Byte[] Encode(Byte[] input)
{
Byte[] output = null;
using (System.IO.MemoryStream msInput = new System.IO.MemoryStream(input))
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
using (BrotliStream bs = new BrotliStream(msOutput, System.IO.Compression.CompressionMode.Compress))
{
bs.SetQuality(11);
bs.SetWindow(22);
msInput.CopyTo(bs);
bs.Close();
output = msOutput.ToArray();
return output;
}
}
要解压缩 brotli 流:
public Byte[] Decode(Byte[] input)
{
using (System.IO.MemoryStream msInput = new System.IO.MemoryStream(input))
using (BrotliStream bs = new BrotliStream(msInput, System.IO.Compression.CompressionMode.Decompress))
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
{
bs.CopyTo(msOutput);
msOutput.Seek(0, System.IO.SeekOrigin.Begin);
output = msOutput.ToArray();
return output;
}
}
为了在网络应用中支持动态压缩,在Global.asax.cs中添加这样的代码:
protected void Application_PostAcquireRequestState(object sender, EventArgs e)
{
var app = Context.ApplicationInstance;
String acceptEncodings = app.Request.Headers.Get("Accept-Encoding");
if (!String.IsNullOrEmpty(acceptEncodings))
{
System.IO.Stream baseStream = app.Response.Filter;
acceptEncodings = acceptEncodings.ToLower();
if (acceptEncodings.Contains("br") || acceptEncodings.Contains("brotli"))
{
app.Response.Filter = new Brotli.BrotliStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "br");
}
else
if (acceptEncodings.Contains("deflate"))
{
app.Response.Filter = new System.IO.Compression.DeflateStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "deflate");
}
else if (acceptEncodings.Contains("gzip"))
{
app.Response.Filter = new System.IO.Compression.GZipStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
}
}