Base64 - CryptoStream with StreamWriter vs Convert.ToBase64S

本文关键字:vs Convert ToBase64S StreamWriter with CryptoStream Base64 | 更新日期: 2023-09-27 18:35:21

根据Alexei的反馈,对问题进行了简化:

如何使用缓冲的 Stream 方法将 CryptoStream 的内容(使用 ToBase64Transform)转换为 StreamWriter(Unicode 编码),而无需使用 Convert.ToBase64String()?

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

Base64 - CryptoStream with StreamWriter vs Convert.ToBase64S

你可能应该实现自定义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 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;
        }