将一个非常大的二进制文件逐步转换为Base64String

时间:2015-09-21 19:20:07

标签: c# base64 zipfile

我需要帮助将非常大的二进制文件(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&# 34; " 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();
        }

解码更加相同。我使用上面base64String变量的大小(根据我测试的原始缓冲区大小而变化)作为解码的缓冲区大小。然后,我调用Convert.ToBase64String()而不是Convert.FromBase64String(),而是写入不同的文件名/路径。

编辑:

我急于减少代码(我将它重构为一个新项目,与其他处理分开以消除对问题不重要的代码)我引入了一个错误。对于所有迭代,应该在secondaryBuffer上执行base 64转换,保存最后一次(由isFinalChunk标识),应该使用buffer。我已经纠正了上面的代码。

编辑#2:

感谢大家的意见/反馈。在纠正了错误之后(参见上面的编辑),我重新测试了我的代码,它现在正在运行。我打算测试并实施@rene的解决方案,因为它看起来是最好的,但我认为我应该让每个人都知道我的发现。

3 个答案:

答案 0 :(得分:12)

根据https://msdn.microsoft.com/en-us/library/ms143350(v=sql.105).aspx the blog中显示的代码,以下代码有效。 Wiktor Zychla

指出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
}

代码使用Ivan Stoev命名空间中的标准类,并使用CryptoStreamSystem.Security.Cryptography及其对应ToBase64Transform

答案 1 :(得分:8)

您可以通过将偏移量和长度传递给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);           
        }
    }
}

以上内容应该有效,但我认为Rene's answer实际上是更好的解决方案。

答案 2 :(得分:1)

使用此代码:

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);

        }
    }
}