将非常大的二进制文件逐步转换为 Base64String
Convert a VERY LARGE binary file into a Base64String incrementally
我需要帮助将一个非常大的二进制文件(ZIP 文件)转换为 Base64String,然后再转换回来。这些文件太大,无法一次全部加载到内存中(它们会抛出 OutOfMemoryExceptions),否则这将是一项简单的任务。我不想单独处理 ZIP 文件的内容,我想处理整个 ZIP 文件。
问题:
我可以将整个 ZIP 文件(目前测试大小从 1 MB 到 800 MB 不等)转换为 Base64String,但是当我将其转换回来时,它已损坏。新的 ZIP 文件大小正确,它被 Windows 和 WinRAR/7-Zip 等识别为 ZIP 文件,我什至可以查看 ZIP 文件内部并看到正确的 sizes/properties,但是当我尝试从 ZIP 文件中提取时,我得到:"Error: 0x80004005",这是一个一般错误代码。
我不确定腐败发生在何处或为何发生。我做了一些调查,我注意到以下几点:
如果您有一个大文本文件,您可以毫无问题地逐步将其转换为 Base64String。如果对整个文件调用 Convert.ToBase64String
产生:"abcdefghijklmnopqrstuvwx",然后对文件分两部分调用它会产生:"abcdefghijkl" 和 "mnopqrstuvwx".
不幸的是,如果文件是二进制文件,则结果不同。虽然整个文件可能会产生:"abcdefghijklmnopqrstuvwx",但尝试将其分成两部分进行处理会产生类似于:"oiweh87yakgb"和 "kyckshfguywp".
有没有办法在避免这种损坏的同时增量地对二进制文件进行 base 64 编码?
我的代码:
private void ConvertLargeFile()
{
FileStream inputStream = new FileStream("C:\Users\test\Desktop\my.zip", FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[MultipleOfThree];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while(bytesRead > 0)
{
byte[] secondaryBuffer = new byte[buffer.Length];
int secondaryBufferBytesRead = bytesRead;
Array.Copy(buffer, secondaryBuffer, buffer.Length);
bool isFinalChunk = false;
Array.Clear(buffer, 0, buffer.Length);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
if(bytesRead == 0)
{
isFinalChunk = true;
buffer = new byte[secondaryBufferBytesRead];
Array.Copy(secondaryBuffer, buffer, buffer.length);
}
String base64String = Convert.ToBase64String(isFinalChunk ? buffer : secondaryBuffer);
File.AppendAllText("C:\Users\test\Desktop\Base64Zip", base64String);
}
inputStream.Dispose();
}
解码大同小异。我使用上面 base64String
变量的大小(取决于我测试的原始缓冲区大小)作为解码的缓冲区大小。然后,我调用 Convert.FromBase64String()
而不是 Convert.ToBase64String()
并写入另一个文件 name/path。
编辑:
我急于减少代码(我将其重构到一个新项目中,与其他处理分开以消除对问题不重要的代码),我引入了一个错误。当应该使用 buffer
时,应在 secondaryBuffer
上对除最后一次迭代(由 isFinalChunk
标识)之外的所有迭代执行 base 64 转换。我已经更正了上面的代码。
编辑#2:
谢谢大家的comments/feedback。纠正错误后(见上面的编辑),我重新测试了我的代码,它现在确实可以工作了。我打算测试和实施@rene 的解决方案,因为它看起来是最好的,但我认为我也应该让每个人都知道我的发现。
您可以通过将偏移量和长度传递给 Convert.ToBase64String
来避免使用辅助缓冲区,如下所示:
private void ConvertLargeFile()
{
using (var inputStream = new FileStream("C:\Users\test\Desktop\my.zip", FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[MultipleOfThree];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while(bytesRead > 0)
{
String base64String = Convert.ToBase64String(buffer, 0, bytesRead);
File.AppendAllText("C:\Users\test\Desktop\Base64Zip", base64String);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
}
}
}
以上应该可行,但我认为 实际上是更好的解决方案。
使用此代码:
public void ConvertLargeFile(string source , string destination)
{
using (FileStream inputStream = new FileStream(source, FileMode.Open, FileAccess.Read))
{
int buffer_size = 30000; //or any multiple of 3
byte[] buffer = new byte[buffer_size];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
byte[] buffer2 = buffer;
if(bytesRead < buffer_size)
{
buffer2 = new byte[bytesRead];
Buffer.BlockCopy(buffer, 0, buffer2, 0, bytesRead);
}
string base64String = System.Convert.ToBase64String(buffer2);
File.AppendAllText(destination, base64String);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
}
}
}
基于the blog from Wiktor Zychla the following code works. This same solution is indicated in the remarks section of Convert.ToBase64String as pointed out by Ivan Stoev
中显示的代码
// using System.Security.Cryptography
private void ConvertLargeFile()
{
//encode
var filein= @"C:\Users\test\Desktop\my.zip";
var fileout = @"C:\Users\test\Desktop\Base64Zip";
using (FileStream fs = File.Open(fileout, FileMode.Create))
using (var cs=new CryptoStream(fs, new ToBase64Transform(),
CryptoStreamMode.Write))
using(var fi =File.Open(filein, FileMode.Open))
{
fi.CopyTo(cs);
}
// the zip file is now stored in base64zip
// and decode
using (FileStream f64 = File.Open(fileout, FileMode.Open) )
using (var cs=new CryptoStream(f64, new FromBase64Transform(),
CryptoStreamMode.Read ) )
using(var fo =File.Open(filein +".orig", FileMode.Create))
{
cs.CopyTo(fo);
}
// the original file is in my.zip.orig
// use the commandlinetool
// fc my.zip my.zip.orig
// to verify that the start file and the encoded and decoded file
// are the same
}
该代码使用 System.Security.Cryptography namespace and uses a CryptoStream
and the FromBase64Transform
中的标准 类 及其对应物 ToBase64Transform
我需要帮助将一个非常大的二进制文件(ZIP 文件)转换为 Base64String,然后再转换回来。这些文件太大,无法一次全部加载到内存中(它们会抛出 OutOfMemoryExceptions),否则这将是一项简单的任务。我不想单独处理 ZIP 文件的内容,我想处理整个 ZIP 文件。
问题:
我可以将整个 ZIP 文件(目前测试大小从 1 MB 到 800 MB 不等)转换为 Base64String,但是当我将其转换回来时,它已损坏。新的 ZIP 文件大小正确,它被 Windows 和 WinRAR/7-Zip 等识别为 ZIP 文件,我什至可以查看 ZIP 文件内部并看到正确的 sizes/properties,但是当我尝试从 ZIP 文件中提取时,我得到:"Error: 0x80004005",这是一个一般错误代码。
我不确定腐败发生在何处或为何发生。我做了一些调查,我注意到以下几点:
如果您有一个大文本文件,您可以毫无问题地逐步将其转换为 Base64String。如果对整个文件调用 Convert.ToBase64String
产生:"abcdefghijklmnopqrstuvwx",然后对文件分两部分调用它会产生:"abcdefghijkl" 和 "mnopqrstuvwx".
不幸的是,如果文件是二进制文件,则结果不同。虽然整个文件可能会产生:"abcdefghijklmnopqrstuvwx",但尝试将其分成两部分进行处理会产生类似于:"oiweh87yakgb"和 "kyckshfguywp".
有没有办法在避免这种损坏的同时增量地对二进制文件进行 base 64 编码?
我的代码:
private void ConvertLargeFile()
{
FileStream inputStream = new FileStream("C:\Users\test\Desktop\my.zip", FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[MultipleOfThree];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while(bytesRead > 0)
{
byte[] secondaryBuffer = new byte[buffer.Length];
int secondaryBufferBytesRead = bytesRead;
Array.Copy(buffer, secondaryBuffer, buffer.Length);
bool isFinalChunk = false;
Array.Clear(buffer, 0, buffer.Length);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
if(bytesRead == 0)
{
isFinalChunk = true;
buffer = new byte[secondaryBufferBytesRead];
Array.Copy(secondaryBuffer, buffer, buffer.length);
}
String base64String = Convert.ToBase64String(isFinalChunk ? buffer : secondaryBuffer);
File.AppendAllText("C:\Users\test\Desktop\Base64Zip", base64String);
}
inputStream.Dispose();
}
解码大同小异。我使用上面 base64String
变量的大小(取决于我测试的原始缓冲区大小)作为解码的缓冲区大小。然后,我调用 Convert.FromBase64String()
而不是 Convert.ToBase64String()
并写入另一个文件 name/path。
编辑:
我急于减少代码(我将其重构到一个新项目中,与其他处理分开以消除对问题不重要的代码),我引入了一个错误。当应该使用 buffer
时,应在 secondaryBuffer
上对除最后一次迭代(由 isFinalChunk
标识)之外的所有迭代执行 base 64 转换。我已经更正了上面的代码。
编辑#2:
谢谢大家的comments/feedback。纠正错误后(见上面的编辑),我重新测试了我的代码,它现在确实可以工作了。我打算测试和实施@rene 的解决方案,因为它看起来是最好的,但我认为我也应该让每个人都知道我的发现。
您可以通过将偏移量和长度传递给 Convert.ToBase64String
来避免使用辅助缓冲区,如下所示:
private void ConvertLargeFile()
{
using (var inputStream = new FileStream("C:\Users\test\Desktop\my.zip", FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[MultipleOfThree];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while(bytesRead > 0)
{
String base64String = Convert.ToBase64String(buffer, 0, bytesRead);
File.AppendAllText("C:\Users\test\Desktop\Base64Zip", base64String);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
}
}
}
以上应该可行,但我认为
使用此代码:
public void ConvertLargeFile(string source , string destination)
{
using (FileStream inputStream = new FileStream(source, FileMode.Open, FileAccess.Read))
{
int buffer_size = 30000; //or any multiple of 3
byte[] buffer = new byte[buffer_size];
int bytesRead = inputStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
byte[] buffer2 = buffer;
if(bytesRead < buffer_size)
{
buffer2 = new byte[bytesRead];
Buffer.BlockCopy(buffer, 0, buffer2, 0, bytesRead);
}
string base64String = System.Convert.ToBase64String(buffer2);
File.AppendAllText(destination, base64String);
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
}
}
}
基于the blog from Wiktor Zychla the following code works. This same solution is indicated in the remarks section of Convert.ToBase64String as pointed out by Ivan Stoev
中显示的代码// using System.Security.Cryptography
private void ConvertLargeFile()
{
//encode
var filein= @"C:\Users\test\Desktop\my.zip";
var fileout = @"C:\Users\test\Desktop\Base64Zip";
using (FileStream fs = File.Open(fileout, FileMode.Create))
using (var cs=new CryptoStream(fs, new ToBase64Transform(),
CryptoStreamMode.Write))
using(var fi =File.Open(filein, FileMode.Open))
{
fi.CopyTo(cs);
}
// the zip file is now stored in base64zip
// and decode
using (FileStream f64 = File.Open(fileout, FileMode.Open) )
using (var cs=new CryptoStream(f64, new FromBase64Transform(),
CryptoStreamMode.Read ) )
using(var fo =File.Open(filein +".orig", FileMode.Create))
{
cs.CopyTo(fo);
}
// the original file is in my.zip.orig
// use the commandlinetool
// fc my.zip my.zip.orig
// to verify that the start file and the encoded and decoded file
// are the same
}
该代码使用 System.Security.Cryptography namespace and uses a CryptoStream
and the FromBase64Transform
中的标准 类 及其对应物 ToBase64Transform