Скомпилируйте Brotli в DLL, на которую может ссылаться .NET.

Поэтому я хотел бы воспользоваться преимуществами Brotli, но я не знаком с Python и C++.

Я знаю, что кто-то скомпилировал его в Windows .exe. Но как мне обернуть его в DLL или что-то, на что может ссылаться приложение .NET? Я знаю, что есть IronPython. Нужно ли мне просто добавить все исходные файлы в проект IronPython и написать адаптер .NET, который вызывает Brotli API и предоставляет их? Но на самом деле я даже не уверен, является ли Brotli API Python или C++.

Глядя на tools/bro.cc, похоже, что методы "входа" определены в encode.c и decode.c как методы BrotliCompress(), BrotliDecompressBuffer(), BrotliDecompressStream(). Поэтому я полагаю, что DLL может быть скомпилирована из классов C++.


person gt6707a    schedule 29.01.2016    source источник
comment
Не уверен, что вы хотели попросить больше ресурсов о Brotli. Он доступен на GitHub.   -  person gt6707a    schedule 06.04.2016
comment
Удалена строка «Для меня это больше не действие» из вашего ответа. SO-вопросы не только для вас. Надеюсь, ты не против.   -  person jgauffin    schedule 25.05.2016
comment
@jgauffin, делающий это через python (все еще из кода .NET\C#), подходит вам?   -  person Evk    schedule 25.05.2016
comment
Декодер написан на C, кодировщик на C++. Нет необходимости включать туда Python, вы можете использовать для этого C++/CLI или, возможно, даже P/Invoke, просто скомпилируйте библиотеку в DLL с помощью VS.   -  person Lucas Trzesniewski    schedule 28.05.2016
comment
Microsoft добавила поддержку Brotli в CorefxLab. Вы найдете образец в моем блоге: meziantou.net/2017/07/17/   -  person meziantou    schedule 28.07.2017


Ответы (3)


Вы можете использовать Brotli.NET, который обеспечивает полную поддержку потоков.

  1. github: https://github.com/XieJJ99/brotli.net/.
  2. Nuget: https://www.nuget.org/packages/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");
                }

            }
       }        
person Jinjun Xie    schedule 24.10.2016
comment
В духе .NET я думаю, что это лучший вариант на данный момент. Тем не менее, WinBrotli, упомянутый в другом ответе, также удовлетворяет. - person gt6707a; 04.11.2016
comment
WinBrotli подходит, когда ввод/вывод невелик. Однако он требует сжатия/распаковки данных в памяти, что означает, что ему требуется гораздо больше памяти и будет иметь более высокую задержку. - person Jinjun Xie; 16.11.2016

Чтобы избежать необходимости в Python, я разветвил исходный код brotli здесь https://github.com/smourier/brotli и создал его версию Windows DLL, которую можно использовать с .NET. Я добавил каталог, содержащий решение WinBrotli Visual Studio 2015 с двумя проектами:

  • WinBrotli: Windows DLL (x86 и x64), которая содержит исходный неизмененный код brotli C/C++.
  • Brotli: консольное приложение Windows (любой процессор), написанное на C# и содержащее код взаимодействия P/Invoke для WinBrotli.

Чтобы повторно использовать Winbrotli DLL, просто скопируйте WinBrotli.x64.dll и WinBrotli.x86.dll (вы можете найти уже собранные выпускные версии в папке WinBrotli/binaries) в сторону приложения .NET и включите BrotliCompression.cs в свой проект C# (или перенесите его на VB или другой язык, если C# не является вашим любимым языком). Код взаимодействия автоматически выберет правильную DLL, соответствующую разрядности текущего процесса (X86 или X64).

Как только вы это сделаете, использовать его довольно просто (ввод и вывод могут быть путями к файлам или стандартными потоками .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 проще взаимодействовать).
  • Добавлен файл WinBrotli.def, который экспортирует 4 функции.
person Simon Mourier    schedule 30.05.2016

Я покажу один из способов сделать это, вызвав нативную библиотеку Python из кода .NET. Что вам нужно:

  1. Вам нужно установить python 2.7 (надеюсь, это очевидно)
  2. Вам нужно скомпилировать brotli из исходников. Надеюсь, это легко. Сначала установите компилятор Microsoft Visual C++ для Python 2.7. Затем клонируйте репозиторий brotli через git clone https://github.com/google/brotli.git и скомпилируйте с помощью python setup.py build_ext. Когда это будет сделано, в каталоге build\lib.win32-2.7 вы найдете файл brotli.pyd. Это модуль Python C++ — он нам понадобится позже.

  3. Вам необходимо либо загрузить двоичные файлы 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, как вы видите выше.

person Evk    schedule 25.05.2016
comment
using (Py.GIL()) {} удалит 7 строк кода! PythonEngine.Shutdown() по-прежнему нужен в конце. - person denfromufa; 27.05.2016
comment
Не знал, что мы здесь участвуем в соревновании по экономии кода :) - person Evk; 27.05.2016
comment
@Evk похоже, тебе нужно поработать над своей ленью еще немного :) - person Lucas Trzesniewski; 28.05.2016
comment
@Evk это не только сокращение строк кода, но и упрощение использования API и пакета. Разве не поэтому не все пишут на C/C++? - person denfromufa; 29.05.2016