【问题标题】:How to Merge two memory streams containing PDF file's data into one如何将两个包含 PDF 文件数据的内存流合并为一个
【发布时间】:2015-08-25 12:41:42
【问题描述】:

我正在尝试将两个 PDF 文件读入两个内存流,然后返回一个包含两个流数据的流。但我似乎不明白我的代码有什么问题。

示例代码:

string file1Path = "Sampl1.pdf";
string file2Path = "Sample2.pdf";
MemoryStream stream1 = new MemoryStream(File.ReadAllBytes(file1Path));
MemoryStream stream2 = new MemoryStream(File.ReadAllBytes(file2Path));
stream1.Position = 0;
stream1.Copyto(stream2);
return stream2;   /*supposed to be containing data of both stream1 and stream2 but contains data of stream1 only*/  

【问题讨论】:

标签: c# itextsharp memorystream


【解决方案1】:

在 PDF 文件的情况下,内存流的合并与 .txt 文件不同。对于 PDF,您需要使用一些 .dll,因为我使用了 iTextSharp.dll(在 AGPL 许可下可用),然后使用此库的功能将它们组合如下:

MemoryStream finalStream = new MemoryStream();
PdfCopyFields copy = new PdfCopyFields(finalStream);
string file1Path = "Sample1.pdf";
string file2Path = "Sample2.pdf";

var ms1 = new MemoryStream(File.ReadAllBytes(file1Path));
ms1.Position = 0;
copy.AddDocument(new PdfReader(ms1));
ms1.Dispose();

var ms2 = new MemoryStream(File.ReadAllBytes(file2Path));
ms2.Position = 0;
copy.AddDocument(new PdfReader(ms2));
ms2.Dispose();
copy.Close();

finalStream 包含 ms1 和 ms2 的合并 pdf。

【讨论】:

  • 这应该是考虑合并文件时的默认设置 - 这是一个特殊情况,当它有效时,而不是当它不起作用时。仅通过将两个文件粘合在一起就可以合并的文件格式很少。事实上,即使是文本文件也不完全适合 - 例如,如果您使用 endlines 来拆分数据(您还需要在文件之间放置分隔符),或者如果它们使用 UTF-8使用 BOM 进行编码(有效地为它们提供某种“标题”),或者即使它们采用两种不同的编码。
  • 我已通过将 “在互联网上免费提供” 更改为 “在 AGPL 许可下提供” 更新了您的答案。 iTextSharp 是许可软件,这意味着它只能在同样根据 AGPL(而非商业许可)发布的项目中免费使用。在商业环境中使用 iTextSharp 后,您必须购买商业许可证才能使用 iTextSharp。
  • @Luaan。你一定是对的。我是编程新手,这是我达到的解决方案。我认为将我的答案发布给寻找类似问题的人会很好。
  • @Bruno Lowagie 谢谢。 :)
【解决方案2】:

注意:

整个问题基于一个错误的前提,即您可以通过合并两个 PDF 文件的二进制文件来生成一个合并的 PDF 文件。例如,这适用于纯文本文件(在一定程度上),但绝对不适用于 PDF。答案仅涉及如何合并两个二进制数据流,而不是如何合并两个 PDF 文件。它按要求回答了 OP 的问题,但实际上并没有解决他的问题。

当您对MemoryStream 使用byte[] 构造函数时,内存流不会随着您添加更多数据而扩展。所以它对于stream1stream2 都不够大。此外,该位置将从零开始,因此您将使用stream1 中的数据覆盖stream2

修复相当简单:

var result = new MemoryStream();
using (var file1 = File.OpenRead(file1Path)) file1.CopyTo(result);
using (var file2 = File.OpenRead(file2Path)) file2.CopyTo(result);

另一种选择是创建您自己的流类,该类将是两个独立流的组合 - 如果您对可组合性感兴趣,这很有趣,但对于像这样简单的事情可能有点矫枉过正:)

只是为了好玩,它可能看起来像这样:

public class DualStream : Stream
{
    private readonly Stream _first;
    private readonly Stream _second;

    public DualStream(Stream first, Stream second)
    {
        _first = first;
        _second = second;
    }

    public override bool CanRead => true;
    public override bool CanSeek => true;
    public override bool CanWrite => false;
    public override long Length => _first.Length + _second.Length;

    public override long Position
    {
        get { return _first.Position + _second.Position; }
        set { Seek(value, SeekOrigin.Begin); }
    }

    public override void Flush() { throw new NotImplementedException(); }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var bytesRead = _first.Read(buffer, offset, count);

        if (bytesRead == count) return bytesRead;

        return bytesRead + _second.Read(buffer, offset + bytesRead, count - bytesRead);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        // To simplify, let's assume seek always works as if over one big MemoryStream
        long targetPosition;

        switch (origin)
        {
            case SeekOrigin.Begin: targetPosition = offset; break;
            case SeekOrigin.Current: targetPosition = Position + offset; break;
            case SeekOrigin.End: targetPosition = Length - offset; break;
            default: throw new NotSupportedException();
        }

        targetPosition = Math.Max(0, Math.Min(Length, targetPosition));

        var firstPosition = Math.Min(_first.Length, targetPosition);
        _first.Position = firstPosition;
        _second.Position = Math.Max(0, targetPosition - firstPosition);

        return Position;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _first.Dispose();
            _second.Dispose();
        }

        base.Dispose(disposing);
    }

    public override void SetLength(long value) 
      { throw new NotImplementedException(); }
    public override void Write(byte[] buffer, int offset, int count) 
      { throw new NotImplementedException(); }
}

主要的好处是,它意味着您不必为了获得一个组合流而分配不必要的内存缓冲区 - 如果您敢的话,它甚至可以直接与文件流一起使用 :D 并且它很容易组合 -您可以制作其他双流的双流,允许您将任意数量的流链接在一起 - 与IEnumerable.Concat 几乎相同。

【讨论】:

  • 那我该怎么办?
  • 不要使用字节构造函数 :D 只使用 new MemoryStream()
  • 不是将 stream1 的位置设置为 0 应该告诉从哪里开始复制吗?
  • @Arsal 是的,这很好 - 问题是stream1 也是在位置零,所以当你开始复制时,它会在@中写入所有现有数据987654331@。您必须先使用stream2.Seek(SeekOrigin.End, 0) - 然后您会收到关于无法扩展使用byte[] 构造函数创建的内存流的错误。
  • @Arsal 你确定它真的被覆盖了吗?你检查过流长度吗?之后你是如何阅读流的?这不像您可以将两个 PDF 文件逐个字节地粘合在一起并得到一个组合文件。尝试对两个文本文件使用相同的代码,这应该会使结果更明显。
猜你喜欢
  • 2012-11-30
  • 1970-01-01
  • 2018-07-30
  • 2014-11-16
  • 2011-04-04
  • 1970-01-01
  • 2013-03-17
  • 1970-01-01
相关资源
最近更新 更多