C#. Разжатие файла при помощи GZip в многопоточной среде

515
28 января 2017, 08:24

Пытаюсь разобраться как написать процесс сжатия и разжатия при помощи GZip в многопоточной среде.

При разжатии файла в многопоточной среде выдает постоянно следующие ошибки: StackOverFlow, System.OutOfMemory.

В режиме отладки на строках:

blockLength = BitConverter.ToInt32(lengthBuffer, 4);

и

_dataSize = BitConverter.ToInt32(compressedData[i], blockLength - 4)

во втором потоке присваиваются космические переменные, которые и ведут к фатальным ошибкам. Пробовал ставить 4 байта, всячески "игрался", пытаясь реализовать по-разному. Итог один. Прошу помощи!. Код ниже.

public abstract class GZip
    {
        protected static bool _cancelled = false;
        protected static bool _success = false;
        protected string sourceFile, destinationFile;
        protected static int _threads = Environment.ProcessorCount;
        protected const int buffer_size = 1024 * 1024;
        protected static int blockSize = 10000000;
        protected static byte[][] lastBuffer = new byte[_threads][];
        protected static byte[][] compressedData = new byte[_threads][];

        public GZip(string input, string output)
        {
            this.sourceFile = input;
            this.destinationFile = output;
        }
        public int CallBackResult()
        {
            if (!_cancelled && _success)
                return 0;
            return 1;
        }
        public void Cancel()
        {
            _cancelled = true;
        }
        public abstract void Launch();
    }
    class Decompressor : GZip
    {
        public Decompressor(string input, string output) : base(input, output)
        {

        }
        public override void Launch()
        {
            Console.Write("Decompressing");
            try
            {
                using (FileStream _compressedFile = new FileStream(sourceFile, FileMode.Open))
                {
                    using (FileStream _decompressedFile = new FileStream(sourceFile.Remove(sourceFile.Length - 3), FileMode.Append))
                    {
                        int blockLength;
                        int _dataSize;
                        byte[] lengthBuffer = new byte[8];
                        Thread[] tPool = new Thread[_threads];
                        while (_compressedFile.Position < _compressedFile.Length)
                        {
                            for (int i = 0; (i < _threads) && (_compressedFile.Position < _compressedFile.Length); i++)
                            {
                                Console.Write(".");
                                _compressedFile.Read(lengthBuffer, 0, lengthBuffer.Length);
                                blockLength = BitConverter.ToInt32(lengthBuffer, 4);
                                compressedData[i] = new byte[blockLength];
                                lengthBuffer.CopyTo(compressedData[i], 0);
                                _compressedFile.Read(compressedData[i], 8, blockLength - 8);
                                _dataSize = BitConverter.ToInt32(compressedData[i], blockLength - 4);
                                lastBuffer[i] = new byte[_dataSize];

                                tPool[i] = new Thread(Decompress);
                                tPool[i].Start(i);
                            }
                            for (int i = 0; (i < _threads) && (tPool[i] != null);)
                            {
                                if (tPool[i].ThreadState == ThreadState.Stopped)
                                {
                                    _decompressedFile.Write(lastBuffer[i], 0, lastBuffer[i].Length);
                                    i++;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error is occured!\n Method: {0}\n Error description {1}", ex.TargetSite, ex.Message);
                _cancelled = true;
            }
        }
        public static void Decompress(object i)
        {
            using (MemoryStream _memoryStream = new MemoryStream(compressedData[(int)i]))
            {
                using (GZipStream cs = new GZipStream(_memoryStream, CompressionMode.Decompress))
                {
                    cs.Read(lastBuffer[(int)i], 0, lastBuffer[(int)i].Length);
                }
            }
        }
    }
}
Answer 1

Ваш код неявно предполагает, что GZipStream'у можно скормить случайный фрагмент сжатого файла, и он его сможет разжать.

Это не так. В сжатом файле есть служебная информация, которую GZipStream не может найти, если ему достаётся лишь кусок файла.

Судя по всему, случайные данные воспринимаются кодом как управляющая информация, что и приводит к проблемам.

Формат gzip состоит из нескольких, кладущихся впритык кусков. Какие бы ни были размеры каждого куска, архиватор распаковывает куски один за одним. В начале каждого куска есть десятибайтный заголовок, содержащий информацию о куске. Но в этом заголовке нет информации о том, какого размера упакованные данные. За заголовком следует информация, упакованная алгоритмом Deflate, после которой — контрольная сумма и размер распакованных данных.

Поэтому склеивать файл из кусков легко, просто можно укладывать их один за другим. А вот для того, чтобы разбить файл на правильные куски, требуется узнать границы куска. Я покамест не вижу, как сделать это, не осуществляя по сути распаковку Deflate.

Ваш код по сути берёт случайные границы кусков, поэтому интерпретировать данные на границе куска как длину нельзя.

READ ALSO
Вложить в ссылку элемент

Вложить в ссылку элемент

Как в ASPNET MVC ссылку вложить элемент, сохраняя при этом виртуальный путь?

390
Ninject не может создать обьект

Ninject не может создать обьект

При попытке создать обьект через контейнер Ninject вылетает exception : Object reference not set to an instance of an objectПодскажите, пожалуйста, в чем может быть проблема?

323
Помогите разобраться с ошибкой

Помогите разобраться с ошибкой

Есть объект, наследованный от IEnumerableStackOverflowException возникает в методе MoveNext этого класса, на строчке с Regex

310