46

我知道如何编码/解码一个简单的字符串到/从base64

但是,如果数据已经写入FileStream对象,我该怎么做。假设我只能访问 FileStream 对象,而不能访问其中先前存储的原始数据。在将 FileStream刷新到文件之前,如何将FileStream 编码为 base64 。

Ofc 我可以在将 FileStream 写入文件后打开我的文件并对其进行编码/解码,但我想一步完成这一切,而不是一个接一个地执行两个文件操作。该文件可能更大,并且在刚刚保存不久之后再次加载、编码和保存它也需要双倍的时间。

也许你们当中有人知道更好的解决方案?例如,我可以将 FileStream 转换为字符串,对字符串进行编码,然后将字符串转换回 FileStream,或者我会做什么以及这样的代码会是什么样子?

4

6 回答 6

45

一种简单的扩展方法

public static class Extensions
{
    public static Stream ConvertToBase64(this Stream stream)
    {
        byte[] bytes;
        using (var memoryStream = new MemoryStream())
        {
            stream.CopyTo(memoryStream);
            bytes = memoryStream.ToArray();
        }

        string base64 = Convert.ToBase64String(bytes);
        return new MemoryStream(Encoding.UTF8.GetBytes(base64));
    }
}
于 2017-09-14T16:16:31.960 回答
35

在处理大型流时,例如超过 4GB 的文件 - 您不想将文件加载到内存中(作为 a Byte[]),因为它不仅速度很慢,而且即使在 64 位进程中也可能导致崩溃Byte[]不能超过 2GB(或 4GB,带gcAllowVeryLargeObjects)。

幸运的是,.NET 中有一个简洁的助手,ToBase64Transform它以块的形式处理流。出于某种原因,Microsoft 将其放入System.Security.Cryptography并实现ICryptoTransform(用于与 一起使用CryptoStream),但忽略它(“任何其他名称的玫瑰......”)只是因为您没有执行任何加密任务。

你像这样使用它CryptoStream

using System.Security.Cryptography;
using System.IO;

//

using( FileStream   inputFile    = new FileStream( @"C:\VeryLargeFile.bin", FileMode.Open, FileAccess.Read, FileShare.None, bufferSize: 1024 * 1024, useAsync: true ) ) // When using `useAsync: true` you get better performance with buffers much larger than the default 4096 bytes.
using( CryptoStream base64Stream = new CryptoStream( inputFile, new ToBase64Transform(), CryptoStreamMode.Read ) )
using( FileStream   outputFile   = new FileStream( @"C:\VeryLargeBase64File.txt", FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 1024 * 1024, useAsync: true ) )
{
    await base64Stream.CopyToAsync( outputFile ).ConfigureAwait(false);
}
于 2019-09-06T10:43:20.140 回答
14

你可以尝试这样事情:

    public Stream ConvertToBase64(Stream stream)
    {
        Byte[] inArray = new Byte[(int)stream.Length];
        Char[] outArray = new Char[(int)(stream.Length * 1.34)];
        stream.Read(inArray, 0, (int)stream.Length);
        Convert.ToBase64CharArray(inArray, 0, inArray.Length, outArray, 0);
        return new MemoryStream(Encoding.UTF8.GetBytes(outArray));
    }
于 2013-10-02T11:38:42.960 回答
12

您还可以将字节编码为 Base64。如何从流中获取此信息,请参见此处:如何在 C# 中将流转换为字节 []?

或者我认为也应该可以使用 .ToString() 方法并对其进行编码。

于 2013-10-02T09:51:08.723 回答
9

一个简单的 Stream 扩展方法可以完成这项工作:

public static class StreamExtensions
{
    public static string ConvertToBase64(this Stream stream)
    {
        if (stream is MemoryStream memoryStream)
        {
            return Convert.ToBase64String(memoryStream.ToArray());
        }

        var bytes = new Byte[(int)stream.Length];

        stream.Seek(0, SeekOrigin.Begin);
        stream.Read(bytes, 0, (int)stream.Length);

        return Convert.ToBase64String(bytes);
    }
}

读取(以及写入)的方法并针对相应的类(无论是文件流、内存流等)进行了优化,并将为您完成工作。对于这样的简单任务,不需要阅读器等。

唯一的缺点是流被复制到字节数组中,但不幸的是,这就是通过 Convert.ToBase64String 转换为 base64 的方式。

于 2020-02-07T08:51:58.930 回答
5

由于文件会更大,因此您在如何执行此操作方面没有太多选择。您无法就地处理文件,因为这会破坏您需要使用的信息。我可以看到您有两个选择:

  1. 读入整个文件,base64 编码,重新写入编码数据。
  2. 以较小的片段读取文件,并在进行时进行编码。编码为同一目录中的临时文件。完成后,删除原始文件,并重命名临时文件。

当然,流的全部意义在于避免这种情况。与其创建内容并将其填充到文件流中,不如将其填充到内存流中。然后对其进行编码,然后才保存到磁盘。

于 2013-10-02T09:46:46.593 回答