如何使用PDFBox 2.0.0从PDF文件中签署InputStream

时间:2015-03-18 13:30:46

标签: java pdf inputstream pdfbox signing

我想在不使用临时文件的情况下从PDF文件中签署一个InputStream 在这里,我将InputStream转换为File,这很好用:

InputStream inputStream = this.signatureObjPAdES.getSignatureDocument().getInputStream();
OutputStream outputStream = new FileOutputStream(new File("C:/temp.pdf"));
int read = 0;
byte[] bytes = new byte[1024];

while ((read = inputStream.read(bytes)) != -1) {
    outputStream.write(bytes, 0, read);
}

PDDocument document = PDDocument.load(new File("C:/temp.pdf"));

...

document.addSignature(new PDSignature(this.dts.getDocumentTimeStamp()), this);
document.saveIncremental(new FileOutputStream("C:/result.pdf");
document.close();

但我想直接这样做:

PDDocument document = PDDocument.load(inputStream);

问题:在运行

Exception in thread "main" java.lang.NullPointerException
at java.io.RandomAccessFile.<init>(Unknown Source)
at org.apache.pdfbox.io.RandomAccessBufferedFileInputStream.<init>(RandomAccessBufferedFileInputStream.java:77)
at org.apache.pdfbox.pdmodel.PDDocument.saveIncremental(PDDocument.java:961)

欢迎所有想法。
谢谢。

编辑: 它现在正在发布PDFBox 2.0.0。

2 个答案:

答案 0 :(得分:3)

原因

直接障碍在方法PDDocument.saveIncremental()本身:

public void saveIncremental(OutputStream output) throws IOException
{
    InputStream input = new RandomAccessBufferedFileInputStream(incrementalFile);
    COSWriter writer = null;
    try
    {
        writer = new COSWriter(output, input);
        writer.write(this, signInterface);
        writer.close();
    }
    finally
    {
        if (writer != null)
        {
            writer.close();
        }
    }
}
     

PDDocument.java

第一行中使用的成员incrementalFile仅在具有PDDocument.load参数的File期间设置。

因此,不能使用此方法。

解决方法

幸运的是,方法PDDocument.saveIncremental()仅使用公开提供的方法和值,但signInterface除外,但您知道它的价值,因为您在代码之前的代码中设置了它{1}}致电:

saveIncremental

因此,您可以在代码中执行等效操作,而不是调用document.addSignature(new PDSignature(this.dts.getDocumentTimeStamp()), this); document.saveIncremental(new FileOutputStream("C:/result.pdf"));

为此,您还需要PDDocument.saveIncremental()的替换值。它需要在

中返回与InputStream input相同内容的流
inputStream

所以你需要两次使用那个流。由于您尚未说明是否可以重置PDDocument document = PDDocument.load(inputStream); ,我们会先将其复制到inputStream,我们会将其转发到byte[]PDDocument.load

因此,请替换

new COSWriter

通过

PDDocument document = PDDocument.load(inputStream);

...

document.addSignature(new PDSignature(this.dts.getDocumentTimeStamp()), this);
document.saveIncremental(new FileOutputStream("C:/result.pdf"));
document.close();

并根据原始byte[] inputBytes = IOUtils.toByteArray(inputStream); PDDocument document = PDDocument.load(new ByteArrayInputStream(inputBytes)); ... document.addSignature(new PDSignature(this.dts.getDocumentTimeStamp()), this); saveIncremental(new FileOutputStream("C:/result.pdf"), new ByteArrayInputStream(inputBytes), document, this); document.close(); 的灵感为您的课程添加新方法saveIncremental

PDDocument.saveIncremental()

侧面

我上面说过

  

由于您尚未说明void saveIncremental(OutputStream output, InputStream input, PDDocument document, SignatureInterface signatureInterface) throws IOException { COSWriter writer = null; try { writer = new COSWriter(output, input); writer.write(document, signatureInterface); writer.close(); } finally { if (writer != null) { writer.close(); } } } 是否可以重置,我们会先将其复制到inputStream,我们会将其转发至byte[]PDDocument.load

实际上还有另外一个原因:new COSWriter检索原始PDF的长度,如下所示:

COSWriter.doWriteSignature()
     

COSWriter.java

long inLength = incrementalInput.available(); 州的文件:

  

请注意,虽然InputStream.available()的某些实现将返回流中的总字节数,但许多实现不会。

要重复使用InputStream而不是使用上述inputStreambyte[],因此,ByteArrayInputStream不仅需要支持inputStream,还需要支持reset()需要成为少数InputStream实现之一,它将流中的总字节数返回为available

FileInputStreamByteArrayInputStream都会将流中的总字节数返回为available

使用通用InputStream而不是这两个时,可能还会有更多问题。

答案 1 :(得分:1)

Cyril Bremaud,你可以使用这种方法,因为PDDocument类有3个重载的构造函数,你可以继续只提供文件路径,如果你喜欢它也可以工作。但是,为了能够将InputStream直接传递给PDDocument构造函数,请使用以下代码:

lStrInputPDFfile = "samples_pdf_signing\Country Calendar.pdf";
lOsPDFInput = new java.io.FileInputStream(lStrInputPDFfile);
jPDFDocument = new org.apache.pdfbox.pdmodel.PDDocument().load(lOsPDFInput);

但这也适用于我的情况:

lStrInputPDFfile = "samples_pdf_signing\Country Calendar.pdf";
jPDFDocument = new org.apache.pdfbox.pdmodel.PDDocument().load(lStrInputPDFfile);

注意:`InputStream是FileInputStream的父类,这就是上述代码的工作原理。

更新了我的代码,请再次检查。感谢@mkl指出了这一点。