我需要帮助将非常大的二进制文件(ZIP文件)转换为Base64String并再次返回.这些文件太大而无法一次性加载到内存中(它们会抛出OutOfMemoryExceptions),否则这将是一项简单的任务.我不想单独处理ZIP文件的内容,我想处理整个ZIP文件.
问题:
我可以将整个ZIP文件(测试大小从1 MB到目前的800 MB)转换为Base64String,但是当我将其转换回来时,它已被破坏.新的ZIP文件大小正确,它被Windows和WinRAR/7-Zip等识别为ZIP文件,我甚至可以查看ZIP文件内部并查看具有正确尺寸/属性的内容,但是当我试图从ZIP文件中提取,我得到:"错误:0x80004005"这是一般错误代码.
我不确定腐败发生的地点或原因.我做了一些调查,我注意到以下几点:
如果您有一个大文本文件,您可以逐步将其转换为Base64String而不会出现问题.如果调用Convert.ToBase64String整个文件产生:"abcdefghijklmnopqrstuvwx",那么在文件中分两部分调用它将产生:"abcdefghijkl"和"mnopqrstuvwx".
不幸的是,如果文件是二进制文件,那么结果就不同了.虽然整个文件可能会产生:"abcdefghijklmnopqrstuvwx",尝试将其分为两部分会产生类似:"oiweh87yakgb"和"kyckshfguywp".
有没有办法逐步基础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();
}
Run Code Online (Sandbox Code Playgroud)
解码更加相同.我使用base64String上面变量的大小(根据我测试的原始缓冲区大小而变化)作为解码的缓冲区大小.然后,Convert.ToBase64String()我调用Convert.FromBase64String()并写入不同的文件名/路径,而不是.
编辑:
我急于减少代码(我将它重构为一个新项目,与其他处理分开,以消除不是问题的核心代码)我介绍了一个错误.应该secondaryBuffer对所有迭代执行base 64转换,保存最后一个(Identified by isFinalChunk),何时buffer应该使用.我已经纠正了上面的代码.
编辑#2:
谢谢大家的意见/反馈.在纠正了错误之后(参见上面的编辑),我重新测试了我的代码,它现在正在运行.我打算测试并实施@rene的解决方案,因为它似乎是最好的,但我认为我应该让每个人都知道我的发现.
ren*_*ene 12
基于所示的代码的博客从Wiktor的Zychla下面的代码工作.如Ivan Stoev所指出的,在Convert.ToBase64String的备注部分中指出了相同的解决方案
// 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
}
Run Code Online (Sandbox Code Playgroud)
该代码使用中发现的标准类System.Security.Cryptography命名空间和使用CryptoStream以及FromBase64Transform与其对应ToBase64Transform
您可以通过传递offset和length来避免使用辅助缓冲区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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
以上应该有效,但我认为Rene的答案实际上是更好的解决方案.
| 归档时间: |
|
| 查看次数: |
5934 次 |
| 最近记录: |