在C#中对PDF进行Base64编码?

38

有人能解释一下如何做到这一点吗?我可以对普通文本或字节数组这样做,但不确定如何处理PDF。我需要先将PDF嵌入字节数组中吗?


2
为什么PDF文件应该与字节数组有所不同呢? - Can Berk Güder
2个回答

63
使用File.ReadAllBytes方法加载PDF文件,然后使用Convert.ToBase64String(bytes)将字节数组编码为普通字符串。
 Byte[] fileBytes = File.ReadAllBytes(@"TestData\example.pdf");
 var content = Convert.ToBase64String(fileBytes);

5
这样使用内存非常浪费,采用基于流的方法会更好。JMarsch提出的基于密码学的方法可能更有效。你也可以每次只读取少量字节(我猜是3的倍数),将它们独立编码,并将它们写入需要的流中。 - Sebastian Good
看看我的上一个评论。缓冲它并不难。 - Andrew Rollings
22
同样地,简洁至上的原则适用于此。没有必要让解决方案比所需更加复杂。如果以上内容符合他的目的(他说它符合),那为什么要变得更加复杂呢?两行 C# 代码和 30 行,如此之差距。 - Andrew Rollings
我认为选择哪种方式取决于具体情况。如果在内存中处理对RAM的影响可以接受,那么就保持简单。另一方面,如果RAM是一个问题(也许是一个大文件,或者是一个可能同时处理数千个请求的服务器进程),那么额外的代码是值得的。就我所知,我找不到任何方法使Encode方法达到30行。如果我按照简单方法的方式计算,它只有10行。因此,2行与10行相比更准确。 - JMarsch
我喜欢这两种方法。我喜欢这种方法的简单性和JMarsch方法的多功能性。我的应用程序需要将(相对较小的)PDF内容编码为Web服务请求,因此我将使用Andrew的方法。 - Mark Ainsworth
显示剩余2条评论

36

有一种方法可以分块处理,这样您就不必一次性占用大量内存。

.Net包括一个可以进行分块的编码器,但它位于一个有点奇怪的位置。它们将其放在System.Security.Cryptography命名空间中。

我测试了下面的示例代码,并且使用我的方法或上面Andrew的方法获得相同的输出。

这是它的工作原理:您启动一个称为CryptoStream的类。这是一种适配器,可插入另一个流中。您将称为CryptoTransform的类插入CryptoStream(反过来附加到文件/内存/网络流),并在从流中读取或写入数据时对数据执行数据转换。

通常,转换是加密/解密,但是.NET还包括ToBase64和FromBase64变换,因此我们不会加密,只是编码。

这里是代码。我包括Andrew建议的(可能命名不当)实现,以便您可以比较输出。


    class Base64Encoder
    {
        // 编码方法:将输入文件转换为Base64编码格式,并输出到指定的输出文件中
        public void Encode(string inFileName, string outFileName)
        {
            // 创建一个ToBase64Transform对象进行加密操作
            System.Security.Cryptography.ICryptoTransform transform = new System.Security.Cryptography.ToBase64Transform();
            
            // 使用using块自动释放资源
            using(System.IO.FileStream inFile = System.IO.File.OpenRead(inFileName),
                                      outFile = System.IO.File.Create(outFileName))
            using (System.Security.Cryptography.CryptoStream cryptStream = new System.Security.Cryptography.CryptoStream(outFile, transform, System.Security.Cryptography.CryptoStreamMode.Write))
            {
                // 创建一个4k大小的缓冲区
                byte[] buffer = new byte[4096];
                int bytesRead;
// 从输入文件中读取数据并加密后写入输出文件中 while ((bytesRead = inFile.Read(buffer, 0, buffer.Length)) > 0) cryptStream.Write(buffer, 0, bytesRead);
cryptStream.FlushFinalBlock(); // 写入最后的块 } }
// 解码方法:将Base64编码格式的输入文件解码成二进制形式,并输出到指定的输出文件中 public void Decode(string inFileName, string outFileName) { // 创建一个FromBase64Transform对象进行解密操作 System.Security.Cryptography.ICryptoTransform transform = new System.Security.Cryptography.FromBase64Transform(); // 使用using块自动释放资源 using (System.IO.FileStream inFile = System.IO.File.OpenRead(inFileName), outFile = System.IO.File.Create(outFileName)) using (System.Security.Cryptography.CryptoStream cryptStream = new System.Security.Cryptography.CryptoStream(inFile, transform, System.Security.Cryptography.CryptoStreamMode.Read)) { byte[] buffer = new byte[4096]; int bytesRead;
// 从输入文件中读取数据并解密后写入输出文件中 while ((bytesRead = cryptStream.Read(buffer, 0, buffer.Length)) > 0) outFile.Write(buffer, 0, bytesRead);
outFile.Flush(); // 清空缓存区并将数据写入磁盘 } }
// 内存编码方法:将输入文件的所有数据一次性读入内存, // 然后将其转换为Base64编码格式,并输出到指定的输出文件中 public void MemoryEncode(string inFileName, string outFileName) { byte[] bytes = System.IO.File.ReadAllBytes(inFileName); // 将输入文件读取到内存中 System.IO.File.WriteAllText(outFileName, System.Convert.ToBase64String(bytes)); // 将读取的数据进行Base64编码后写入输出文件中 } }

我也在尝试着调整加密流的附加位置。在Encode方法中,我将它附加到输出(写入)流上,因此在实例化CryptoStream时,我使用它的Write()方法。

当我读取时,我将其附加到输入(读取)流上,因此我在CryptoStream上使用read方法。无论附加到哪个流上都没有太大关系。我只需要将适当的Read或Write枚举成员传递给CryptoStream的构造函数即可。


我还没有运行和验证过这个,但它看起来非常有前途、好极了。很酷的想法!+1 - codingbear

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接