Зачем? Ошибка C# после Refactor: "Поток недействителен или соответствующая подпись не найдена".

Это следующий вопрос: распакуйте поток в строку, используя SevenZipSharp.

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

using System;
using System.IO;
using SevenZip;

namespace _7ZipWrapper
{
    public class ProgramOriginal
    {
        public static void Main()
        // This should be broken into separate methods
        {
            // Setup Input String
            var strToCompress = "This String"; // will pass as parameter
            var memStreamToCompress = new MemoryStream();

            var StringToStream = new StreamWriter(memStreamToCompress);
            StringToStream.Write(strToCompress);
            StringToStream.Flush();

            // Confirm the Input Stream is As-Expected
            memStreamToCompress.Position = 0;
            var MemoryAsString = new StreamReader(memStreamToCompress);
            Console.WriteLine("Stream in memory: " + MemoryAsString.ReadToEnd());
            Console.ReadKey();

            // Setup the SevenZip Dll
            SevenZipCompressor.SetLibraryPath(@"C:\Temp\7za64.dll");

            // Set up a compressor...
            SevenZipCompressor SevenZipC = new SevenZipCompressor();
            SevenZipC.CompressionMethod = CompressionMethod.Ppmd;
            SevenZipC.CompressionLevel = global::SevenZip.CompressionLevel.Ultra;
            SevenZipC.ScanOnlyWritable = true;

            // Compress PassedStream -> CompressedStream
            var compressedMemoryStream = new MemoryStream();
            SevenZipC.CompressStream(memStreamToCompress, compressedMemoryStream, "Optional Password Field");
            compressedMemoryStream.Position = 0;
            StreamReader compressedMemoryAsString = new StreamReader(compressedMemoryStream);

            // Show that we have a compressed String
            compressedMemoryStream.Position = 0;
            Console.WriteLine(compressedMemoryAsString.ReadToEnd());
            Console.ReadKey();

            // Set up a decompressor... (only needs to know what to decompress)
            compressedMemoryStream.Position = 0;
            var SevenZipE = new SevenZip.SevenZipExtractor(compressedMemoryStream, "Optional Password Field");

            // Decompress CompressedStream
            var DecompressedMemoryStream = new MemoryStream();
            SevenZipE.ExtractFile(0, DecompressedMemoryStream);

            // Show that DecompressedMemoryStream is valid
            DecompressedMemoryStream.Position = 0;
            StreamReader decompressedStreamAsText = new StreamReader(DecompressedMemoryStream);
            Console.WriteLine("Decompressed String: " + decompressedStreamAsText.ReadToEnd());
            Console.ReadKey();
        }
    }
}

Тем не менее, приведенный выше код явно не приносит пользы в его текущей форме (он предназначен для COM DLL).

Я думал, что у меня дома и в доме, и что рефакторинг будет трудным делом. Однако мои попытки преобразовать код во что-то полезное оставили меня в растерянности из-за того, что следующий код выдает исключение System.ArgumentException ('Поток неверный или соответствующая подпись не найдена.'), но приведенный выше код работает без проблем.

В принципе, должно быть какое-то различие, но я не знаю, что его вызывает и как решить. Решение, сопровождаемое кратким объяснением, будет высоко ценится.

using System;
using System.IO;
using SevenZip;

namespace _7ZipWrapper
{
    public class Program
    {
        public static void Main()
        {
            // Setup Input String
            var strToCompress = "This String"; // will eventually pass as parameter


            // Convert input string to memory stream
            var memStreamToCompress = StringToStream(strToCompress);


            // Confirm the Input Stream is As-Expected
            memStreamToCompress.Position = 0;
            var MemoryAsString = new StreamReader(memStreamToCompress);
            Console.WriteLine("Stream in memory: " + MemoryAsString.ReadToEnd());
            Console.ReadKey();

            // Compress the Stream
            memStreamToCompress.Position = 0;
            var compressedString = StreamCompress(memStreamToCompress, "password");

            // Decompress the String
            var memStreamToRestore = StringToStream(compressedString);
            memStreamToRestore.Position = 0;

            var decompressedString = StreamDecompress(memStreamToRestore, "password");

            Console.WriteLine(decompressedString);
            Console.ReadKey();
        }

        private static MemoryStream StringToStream(string strToMemoryStream)
        {
            var memoryStream = new MemoryStream();
            var writer = new StreamWriter(memoryStream);
            writer.Write(strToMemoryStream);
            writer.Flush();
            return memoryStream;
        }

        private static MemoryStream StringToStream1(string strToMemoryStream)
        {
            var memoryStream = new MemoryStream();
            var writer = new StreamWriter(memoryStream);
            writer.Write(strToMemoryStream);
            writer.Flush();
            return memoryStream;
        }


        private static string StreamCompress(MemoryStream memStreamToCompress, string optionalPassword)
        {
            // Setup the SevenZip Dll
            SevenZipCompressor.SetLibraryPath(@"C:\Temp\7za64.dll");

            // Set up a compressor...
            SevenZipCompressor SevenZip = new SevenZipCompressor();
            SevenZip.CompressionMethod = CompressionMethod.Ppmd;
            SevenZip.CompressionLevel = global::SevenZip.CompressionLevel.Ultra;
            SevenZip.ScanOnlyWritable = true;

            // Compress PassedStream -> CompressedStream
            var compressedMemoryStream = new MemoryStream();
            SevenZip.CompressStream(memStreamToCompress, compressedMemoryStream, optionalPassword); // "Optional Password Field"
            compressedMemoryStream.Position = 0;
            StreamReader compressedMemoryAsString = new StreamReader(compressedMemoryStream);
            return compressedMemoryAsString.ReadToEnd();
        }

        private static string StreamDecompress(MemoryStream compressedMemoryStream, string optionalPassword)
        {
            // Setup the SevenZip Dll
            SevenZipExtractor.SetLibraryPath(@"C:\Temp\7za64.dll");

            // Set up a decompressor... (only needs to know what to decompress)
            compressedMemoryStream.Position = 0;
            // CRASHES Next Line: System.ArgumentException: 'The stream is invalid or no corresponding signature was found.'
            var SevenZip = new SevenZip.SevenZipExtractor(compressedMemoryStream, optionalPassword);  

            // Decompress CompressedStream
            var DecompressedMemoryStream = new MemoryStream();
            SevenZip.ExtractFile(0, DecompressedMemoryStream);

            // Show that DecompressedMemoryStream is valid
            DecompressedMemoryStream.Position = 0;
            StreamReader decompressedStreamAsText = new StreamReader(DecompressedMemoryStream);
            return decompressedStreamAsText.ReadToEnd();
        }
    }
}

Примечание. Хотя я понимаю, что с этим кодом я могу столкнуться с множеством проблем, и собираюсь сделать следующую итерацию в CodeReview. Тем не менее, не стесняйтесь предлагать предложения, но сейчас это учебное упражнение для меня. ТИА

2 ответа

Решение

Ваш рабочий код распакован compressedMemoryStream, Ваш неработающий код распаковал строку, созданную из compressedMemoryStream,

Как я уже говорил в вашем предыдущем вопросе, результатом сжатия является не текст. Если вы должны представить его как текст, вы должны использовать Base64 или hex. Но просто читать из него, как будто это текст в кодировке UTF-8 (что вы сейчас делаете), просто не будет работать.

Результат вашего StreamCompress Метод, вероятно, должен быть byte[], Это легко достичь:

// Note: changed input type to just Stream to make it more general
private static byte[] StreamCompress(Stream input, string optionalPassword)
{
    SevenZipCompressor.SetLibraryPath(@"C:\Temp\7za64.dll");
    SevenZipCompressor SevenZip = new SevenZipCompressor();
    SevenZip.CompressionMethod = CompressionMethod.Ppmd;
    SevenZip.CompressionLevel = global::SevenZip.CompressionLevel.Ultra;
    SevenZip.ScanOnlyWritable = true;

    var output = new MemoryStream();
    SevenZip.CompressStream(input, output, optionalPassword);
    // You don't even need to rewind when you call ToArray
    return output.ToArray();
}

Когда вы хотите распаковать, вы можете просто создать MemoryStream обернуть этот байтовый массив, как один из многих вариантов.

Если вам действительно нужен результат в виде строки, вы можете вызвать Convert.ToBase64Stringпотом позвони Convert.FromBase64String чтобы вернуть оригинальные байты. Это не приведет к потере информации, в отличие от вашего текущего подхода.

Я также должен отметить, что если вы не хотите использовать сжатие, специфичное для 7zip, существует также множество полностью управляемых библиотек сжатия.

C# сжать и распаковать строку с помощью SevenZipSharp

Основная проблема заключалась в том, что я не использовал строки должным образом, преобразование потока памяти в строку не работает. Решение использует кодировку base64, чтобы сделать сжатую строку переносимой; это позволяет хранить его в файле XML / JSON, который соответствует моим потребностям. Спасибо @Daisy Shipton (см.: этот ответ).

Исходя из VBA, использование конструкторов (передача аргумента при обновлении) не сразу стало очевидным, но это помогает. Это был ключ:

// Create a memory stream from the input: base64 --> bytes --> memStream
Byte[] compBytes = Convert.FromBase64String(input);
MemoryStream compStreamIn = new MemoryStream(compBytes);

В надежде, что это поможет кому-то еще:

using System;
using System.IO;
using System.Text;
using SevenZip;

namespace _7ZipWrapper
{
    public class ProgramToModify
    {
        public static void Main()
        {
            var input = "Some string"; // Input String will pass as parameter

            var compressed = MyEncode(input);
            Console.WriteLine("Compressed String: " + compressed);

            var decodedString = myDecode(compressed);
            Console.WriteLine("Decompressed String: " + decodedString);
            Console.ReadKey();
        }

        // Returns compressed and encoded base64 string from input 
        private static String MyEncode(string input) 
        {
            // Setup the SevenZip Dll
            SevenZipCompressor.SetLibraryPath(@"C:\Temp\7za64.dll");
            SevenZipCompressor SevenZipC = new SevenZipCompressor();
            SevenZipC.CompressionMethod = CompressionMethod.Ppmd;
            SevenZipC.CompressionLevel = global::SevenZip.CompressionLevel.Ultra;
            SevenZipC.ScanOnlyWritable = true;

            var memoryStream = new MemoryStream(); // Create Memory Stream
            var streamWriter = new StreamWriter(memoryStream);
            streamWriter.Write(input); // streamWriter writes input to memoryStream
            streamWriter.Flush();

            // Compress: memoryStream -> cmpdMemoryStream
            var cmpdMemoryStream = new MemoryStream();
            SevenZipC.CompressStream(memoryStream, cmpdMemoryStream, "Optional Password Field");

            Byte[] bytes = cmpdMemoryStream.ToArray();
            return Convert.ToBase64String(bytes);
        }


        // Returns plain string from compressed and encoded input
        private static String myDecode(string input) 
        {
            // Create a memory stream from the input: base64 --> bytes --> memStream
            Byte[] compBytes = Convert.FromBase64String(input);
            MemoryStream compStreamIn = new MemoryStream(compBytes);

            SevenZipExtractor.SetLibraryPath(@"C:\Temp\7za64.dll");
            var SevenZipE = new SevenZip.SevenZipExtractor(compStreamIn, "Optional Password Field");

            var OutputStream = new MemoryStream();
            SevenZipE.ExtractFile(0, OutputStream);

            var OutputBase64 = Convert.ToBase64String(OutputStream.ToArray());
            Byte[] OutputBytes = Convert.FromBase64String(OutputBase64);
            string output = Encoding.UTF8.GetString(OutputBytes);
            return output;
        }
    }
}
Другие вопросы по тегам