fee*_*all 29 c# string base64 file filestream
我知道如何编码/解码一个简单的字符串到/从base64.
但是,如果数据已经写入FileStream对象,我该怎么做呢?假设我只能访问FileStream对象,而不能访问之前存储的原始数据.在将FileStream刷新到文件之前,如何将FileStream编码为base64.
在我将FileStream写入文件之后,我可以打开我的文件并对其进行编码/解码,但我想一步一步完成所有这一切,而不是一个接一个地执行两个文件操作.该文件可能更大,并且在刚刚保存一段时间后再加载,编码并再次保存它也需要两倍的时间.
也许有人认识一个更好的解决方案?我可以将FileStream转换为字符串,对字符串进行编码,然后将字符串转换回FileStream,例如,我将做什么以及这样的代码将如何?
Dai*_*Dai 30
在处理大流时,例如大小超过 4GB 的文件 - 您不想将文件加载到内存中(作为Byte[]),因为它不仅非常慢,而且可能导致崩溃,即使在 64 位进程中Byte[]不能超过 2GB(或 4GB 与gcAllowVeryLargeObjects)。
幸运的是,.NET 中有一个巧妙的帮助程序ToBase64Transform,它可以分块处理流。出于某种原因,微软将它放入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);
}
Run Code Online (Sandbox Code Playgroud)
chr*_*389 18
一个简单的扩展方法
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));
}
}
Run Code Online (Sandbox Code Playgroud)
Jac*_*cob 15
您可以尝试一些类似的是:
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));
}
Run Code Online (Sandbox Code Playgroud)
alp*_*am8 11
您还可以将字节编码为Base64.如何从流中获取此信息,请参见此处:如何在C#中将Stream转换为byte []?
或者我认为也应该使用.ToString()方法并对其进行编码.
一个简单的 Stream 扩展方法就可以完成这项工作:
public static class StreamExtensions
{
public static string ConvertToBase64(this Stream stream)
{
var bytes = new Byte[(int)stream.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(bytes, 0, (int)stream.Length);
return Convert.ToBase64String(bytes);
}
}
Run Code Online (Sandbox Code Playgroud)
读取(以及写入)方法和针对相应类(无论是文件流、内存流等)进行优化的方法将为您完成工作。对于像这样的简单任务,不需要读者等等。
唯一的缺点是流被复制到字节数组中,但这就是通过 Convert.ToBase64String 转换为 Base64 的工作原理。
建议使用的答案ToBase64Transform是有效的,但有一个很大的问题。不确定这是否应该是一个答案,但如果我知道这一点,它会节省我很多时间。
我遇到的问题ToBase64Transform是它被硬编码为一次读取 3 个字节。如果对构造函数中指定的输入流的每次写入都CryptoStream类似于 websocket 或具有不小的开销或延迟的任何内容,那么这可能是一个巨大的问题。
底线 - 如果你正在做这样的事情:
using var cryptoStream = new CryptoStream(httpRequestBodyStream, new ToBase64Transform(), CryptoStreamMode.Write);
Run Code Online (Sandbox Code Playgroud)
可能值得分叉该类ToBase64Transform以将硬编码的 3/4 字节值修改为更大的值,从而减少写入次数。在我的例子中,使用默认的 3/4 值,传输速率约为 100 KB/s。更改为 768/1024(相同比率)有效,并且由于写入次数减少,传输速率约为 50-100 MB/s。
public class BiggerBlockSizeToBase64Transform : ICryptoTransform
{
// converting to Base64 takes 3 bytes input and generates 4 bytes output
public int InputBlockSize => 768;
public int OutputBlockSize => 1024;
public bool CanTransformMultipleBlocks => false;
public virtual bool CanReuseTransform => true;
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
ValidateTransformBlock(inputBuffer, inputOffset, inputCount);
// For now, only convert 3 bytes to 4
byte[] tempBytes = ConvertToBase64(inputBuffer, inputOffset, 768);
Buffer.BlockCopy(tempBytes, 0, outputBuffer, outputOffset, tempBytes.Length);
return tempBytes.Length;
}
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
ValidateTransformBlock(inputBuffer, inputOffset, inputCount);
// Convert.ToBase64CharArray already does padding, so all we have to check is that
// the inputCount wasn't 0
if (inputCount == 0)
{
return Array.Empty<byte>();
}
// Again, for now only a block at a time
return ConvertToBase64(inputBuffer, inputOffset, inputCount);
}
private byte[] ConvertToBase64(byte[] inputBuffer, int inputOffset, int inputCount)
{
char[] temp = new char[1024];
Convert.ToBase64CharArray(inputBuffer, inputOffset, inputCount, temp, 0);
byte[] tempBytes = Encoding.ASCII.GetBytes(temp);
if (tempBytes.Length != 1024)
throw new Exception();
return tempBytes;
}
private static void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
if (inputBuffer == null) throw new ArgumentNullException(nameof(inputBuffer));
}
// Must implement IDisposable, but in this case there's nothing to do.
public void Dispose()
{
Clear();
}
public void Clear()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) { }
~BiggerBlockSizeToBase64Transform()
{
// A finalizer is not necessary here, however since we shipped a finalizer that called
// Dispose(false) in desktop v2.0, we need to keep it in case any existing code had subclassed
// this transform and expects to have a base class finalizer call its dispose method.
Dispose(false);
}
}
Run Code Online (Sandbox Code Playgroud)
由于文件较大,因此您无法选择如何执行此操作.您无法处理该文件,因为这会破坏您需要使用的信息.您可以看到两个选项:
当然,流的全部意义在于避免这种情况.而不是创建内容并将其填充到文件流中,而是将其填充到内存流中.然后编码,然后保存到磁盘.
| 归档时间: |
|
| 查看次数: |
68347 次 |
| 最近记录: |