Ошибка outofmemory при декомпрессии ab (Android Backup) файла

390
16 января 2017, 18:43

На англоязычной версии сайта нашел метод, который мне очень подходит:

 public static byte[] Decompress(byte[] gzip)
        {
            using (var stream = new Ionic.Zlib.ZlibStream(new MemoryStream(gzip), Ionic.Zlib.CompressionMode.Decompress))
            {
                const int size = 1024;
                byte[] buffer = new byte[size];
                using (MemoryStream memory = new MemoryStream())
                {
                    int count = 0;
                    do
                    {
                        count = stream.Read(buffer, 0, size);
                        if (count > 0)
                        {
                            memory.Write(buffer, 0, count);
                        }
                    }
                    while (count > 0);
                    return memory.ToArray();
                }
            }
        }

Для вызова метода пользуюсь

private void converttotarTool_Click(object sender, EventArgs e)
        {
        byte[] app = Decompress(File.ReadAllBytes(@"D:\ab\2017-01-03_141454.ab"));
        File.WriteAllBytes(@"D:\ab\backup.tar", app);
    }

Если конвертировать в ".tar" бекапы небольшого размера, то проблем не возникает. В моем случае бекап 1,6 гб - возникает ошибка outofmemory. Подскажите, как исправить метод чтобы все содержимое закидывалось в оперативную память частями?

Answer 1

Навряд ли поможет, но стоит попробовать сразу в файл записывать сжатые куски.

ZlibStream можно инициализировать любым стримом, так что не обязательно весь файл в памяти держать, а только куски по 1024, как у Вас и было, и записывать сжатый результат сразу в выходной файл:

    public static void Decompress(string input, string output)
    {
        using (var stream = new Ionic.Zlib.ZlibStream(File.OpenRead(input), Ionic.Zlib.CompressionMode.Decompress))
        {
            const int size = 1024;
            byte[] buffer = new byte[size];
            using (FileStream file = File.Create(output))
            {
                int count = 0;
                do
                {
                    count = stream.Read(buffer, 0, size);
                    if (count > 0)
                        file.Write(buffer, 0, count);
                } while (count > 0);
            }
        }
    }
Answer 2

В дополнение к правильному ответу @Umed: вы не должны проводить ваши операции в памяти. У вас информация содержится в нескольких местах: она читается из файла в массив вся, потом из массива делается MemoryStream, который содержит копию информации, потом другой MemoryStream содержит декомпрессированную информацию, потом массив содержит копию декомпрессированной информации, и это всё сбрасывается в файл. Для «поточных» операций по кускам имеет смысл, как правильно написано в другом ответе, использовать именно потоки.

Результирующий код может быть таким (упростил пример @Umed):

public static void Decompress(string input, string output)
{
    using (FileStream inputStream = File.OpenRead(input))
    using (var zlibStream = new Ionic.Zlib.ZlibStream(inputStream,
                                                      Ionic.Zlib.CompressionMode.Decompress))
    using (FileStream outputStream = File.Create(output))
        zlibStream.CopyTo(outputStream);
}
READ ALSO
Действия формы авторизации без сохранения данных

Действия формы авторизации без сохранения данных

Знатоки, может у Вас есть идеи, как реализовать это?

315
C# Form не прогружается

C# Form не прогружается

Форма WaitingDialogcs не прогружается, но когда метод GetPage законьчил работу загрузалиась

274
Загрузка картинки в MySQL базу ASP NET MVC 5

Загрузка картинки в MySQL базу ASP NET MVC 5

приветподскажите как загрузить картинку в базу MySQL

323
Условие - пока не [требует правки]

Условие - пока не [требует правки]

Если тхт1 имеет строку 57, и в тхт2 есть строки от 00 до 09(01, 02, 03, 04,(запятая как энтер) итдто софт должен считать каждую строку , хешарнуть ее и сравнить...

357