Base64 - 带StreamWriter和Convert.ToBase64String()的CryptoStream

时间:2013-10-14 16:18:50

标签: c# base64 streamwriter cryptostream

根据阿列克谢的反馈,简化了问题:

如何在不使用Convert.ToBase64String()的情况下使用缓冲流方法将CryptoStream(使用ToBase64Transform)的内容转换为StreamWriter(Unicode编码)?

注意:调用Convert.ToBase64String()会抛出OutOfMemoryException,因此需要使用缓冲/流方法进行转换。

2 个答案:

答案 0 :(得分:2)

您可能应该实现自定义Stream,而不是TextWriter。组合流比编写器容易得多(比如将流传递给压缩流)。

创建自定义流 - 从Stream派生并至少实现WriteFlush(如果您需要R / W流,则实现Read)。其余的或多或少是可选的,取决于您的额外需求,定期复制到其他流不需要任何其他内容。

  • 在构造函数中获取内部流传递给您写入。 Base64总是生成ASCII字符,所以很容易将带有或不带BOM的UTF-8输出直接写入流,但是如果要指定编码,则可以在内部用StreamWriter封装内部流。

  • 在你的Write实现缓冲区数据中,直到你得到足够的字节来拥有3个字节的多个块(即300个)并在该部分上调用Convert.ToBase64String。确保不要松开尚未转换的部分。由于Base64将3个字节转换为4个字符,因此以3个大小的多个块进行转换,最后将不会有= / ==填充,并且可以与下一个块连接。所以将转换后的部分写入内部流/写入器。请注意,您希望将块大小限制为相对较小的值,如3*10000,以避免在大型对象堆上分配块。

  • Flush中确保转换最后一个未写入的字节(这将是唯一一个在末尾加=填充的字节)并将其写入流中。

  • 对于阅读,您可能需要更加小心,因为在Base64中允许使用空格,因此您无法读取固定数量的字符并转换为字节。最简单的方法是从StreamReader按字符读取,并将每4个非空格转换为字节。

注意:您可以考虑直接从字节手写/读取Base64。它会给你带来一些性能上的好处,但如果你不善于移位,可能会很难。

答案 1 :(得分:0)

请尝试使用以下加密。我使用fileName / filePath作为输入。您可以根据自己的要求进行调整。使用这个我已成功加密超过1 GB的文件,没有任何内存不足。

public bool EncryptUsingStream(string inputFileName, string outputFileName)
        {
            bool success = false;

            // here assuming that you already have key
            byte[] key = new byte[128];

            SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create();
            algorithm.Key = key;

            using (ICryptoTransform transform = algorithm.CreateEncryptor())
            {
                CryptoStream cs = null;
                FileStream fsEncrypted = null;
                try
                {
                    using (FileStream fsInput = new FileStream(inputFileName, FileMode.Open, FileAccess.Read))
                    {
                        //First write IV 
                        fsEncrypted = new FileStream(outputFileName, FileMode.Create, FileAccess.Write);
                        fsEncrypted.Write(algorithm.IV, 0, algorithm.IV.Length);

                        //then write using stream
                        cs = new CryptoStream(fsEncrypted, transform, CryptoStreamMode.Write);
                        int bytesRead;
                        int _bufferSize = 1048576; //buggersize = 1mb; 
                        byte[] buffer = new byte[_bufferSize];
                        do
                        {
                            bytesRead = fsInput.Read(buffer, 0, _bufferSize);
                            cs.Write(buffer, 0, bytesRead);
                        } while (bytesRead > 0);

                        success = true;

                    }
                }
                catch (Exception ex)
                {
                    //handle exception or throw.
                }
                finally
                {
                    if (cs != null)
                    {                       
                        cs.Close();
                        ((IDisposable)cs).Dispose();                    

                        if (fsEncrypted != null)
                        {
                            fsEncrypted.Close();
                        }
                    }

                }
            }
            return success;
        }