我创建此方法以签署pdf文件:
public File sign7(File pdfOriginal,
String diretorioSalvar,
String nomeArquivo,
String motivo, String local,
LocalDateTime data,
String textoAssinatura,
boolean visible,
PDFService.DisposicaoPagina dispPagina,
File arquivoOriginal
) throws IOException, DocumentException, GeneralSecurityException {
log.debug("comecou assinar");
File diretorioSaida = new File(diretorioSalvar);
diretorioSaida.mkdirs();
File pdfAssinado = new File(diretorioSalvar+File.separator+nomeArquivo);
String keystore_password = KEYSTORE_PASSWORD;
String key_password = KEYSTORE_PASSWORD;
keystore.load(KEYSTORE.getInputStream(), keystore_password.toCharArray());
PrivateKey key = (PrivateKey) keystore.getKey(alias, key_password.toCharArray());
Certificate[] chain = keystore.getCertificateChain(alias);
log.debug("keystore provider : {}", keystore.getProvider().getName());
log.debug("Assinando com alias :{}", alias);
log.debug("chain size: " + chain.length);
PdfReader reader = new PdfReader(new RandomAccessBufferedFileInputStream(pdfOriginal));
PdfWriter writer = new PdfWriter(pdfAssinado);
PdfDocument doc = new PdfDocument(reader,writer);
FileOutputStream os = new FileOutputStream(pdfAssinado);
PdfSigner signer = new PdfSigner(doc.getReader(),os,true);
signer.setCertificationLevel(PdfSigner.NOT_CERTIFIED);
//TEXTO DO CARIMBO
String texto;
ImageData imgCarimbo;
PdfPage moldPage = doc.getLastPage();
PageSize pSize = new PageSize(moldPage.getPageSize());
PdfCanvas cPage = new PdfCanvas(moldPage);
PdfFont font = null;
try {
font = PdfFontFactory.createFont(FONT.getFile().getPath(), PdfEncodings.WINANSI, true);
} catch (IOException e) {
e.printStackTrace();
}
cPage.setFillColor(Color.BLACK);
Rectangle rect = new Rectangle(
(float) (pSize.getWidth()*0.653), //0.725 Y
(float) (pSize.getHeight()*0.9), //0.90 X
(float) (pSize.getWidth()*0.32), //0.25 Largura
(float) (pSize.getHeight()*0.068)); //0.07 Altura
cPage.fillStroke();
PdfFormXObject xObject = new PdfFormXObject(rect);
Image rectImg = new Image(xObject);
ImageData imgLogoCarimboOval = ImageDataFactory.create(LOGO_CARIMBO_DIGITAL.getFile().getPath());
ImageData imgLogoCarimboBg = ImageDataFactory.create(LOGO_CARIMBO_BG.getFile().getPath());
int paginaAparencia = (dispPagina == PDFService.DisposicaoPagina.ULTIMA_PAGINA?doc.getNumberOfPages():1);
String arqOriginalHash = "";
if (arquivoOriginal != null) {
arqOriginalHash = pdfService.gerarHash(arquivoOriginal);
}
PdfSignatureAppearance appearance = signer
.getSignatureAppearance()
.setReason(motivo + " - Hash: " + arqOriginalHash)
.setLocation(local)
.setReuseAppearance(false)
.setImage(imgLogoCarimboBg)
.setSignatureGraphic(imgLogoCarimboOval)
.setImageScale(100)
.setRenderingMode(RenderingMode.GRAPHIC_AND_DESCRIPTION)
.setPageRect(rect)
.setLayer2Font(font)
.setLayer2FontSize(6)
.setLayer2Text(textoAssinatura)
.setPageNumber(paginaAparencia);
signer.setFieldName(signer.getNewSigFieldName());
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(key, DigestAlgorithms.SHA1, "BC");
IExternalDigest digest = new ProviderDigest("BC");
Collection<ICrlClient> crlList=null; IOcspClient ocspClient = null; ITSAClient tsaClient=null;
writer.close();
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, 0, PdfSigner.CryptoStandard.CMS);
reader.close();
os.close();
log.debug("acabou assinar");
return pdfAssinado;
}
(此方法在我的最后一页中创建一个图章并签署pdf),但是当我尝试签署一个500MB的文件时,我得到了一个Java堆空间:
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, 0, PdfSigner.CryptoStandard.CMS);
如果我尝试对较小的文件进行签名(我只能一一尝试,不知道是否同时尝试了多个,那么我会遇到相同的错误)
我已经成功尝试从我的应用程序更改内存。
答案 0 :(得分:1)
PdfReader
实例化在您的PdfReader
实例中,您混合使用了PDF库,结果是需要额外的存储空间来存储原始文件的副本。对于File pdfOriginal
,您可以这样做:
PdfReader reader = new PdfReader(new RandomAccessBufferedFileInputStream(pdfOriginal));
RandomAccessBufferedFileInputStream
不是iText类!但是,我假设您在这里使用了这个名称的PDFBox类。这个PDFBox类是InputStream
,它还实现了PDFBox的RandomAccessRead
接口,该接口以通用的随机访问方式在本地文件系统文件上工作。
由于iText确实具有实现文件随机访问的机制,并且特别是为此不使用PDFBox接口,因此它仅识别并使用RandomAccessBufferedFileInputStream
实例作为InputStream
。因此,iText将从该Stream中读取所有数据到byte[]
中,以提供适当的随机访问支持。
相反,如果允许iText查看源是本地文件系统文件,则它可以使用其自己的随机文件访问权限,并且不会创建该文件的内存中副本。只需使用
PdfReader reader = new PdfReader(pdfOriginal);
对于您的 500MB文件,这将减少500 MB的内存使用量。
PdfWriter
和PdfDocument
实例此外,您知道
PdfWriter writer = new PdfWriter(pdfAssinado);
PdfDocument doc = new PdfDocument(reader,writer);
FileOutputStream os = new FileOutputStream(pdfAssinado);
PdfSigner signer = new PdfSigner(doc.getReader(),os,true);
即您自己创建了一个PdfWriter
和一个PdfDocument
实例,它们仅消耗额外的内存,然后创建了一个PdfSigner
及其内部PdfWriter
和PdfDocument
实例。< / p>
因此,请勿创建自己的PdfWriter
和PdfDocument
实例。您稍后可以访问自己的PdfDocument
实例来确定页面大小;您应该改用PdfSigner
FileOutputStream os = new FileOutputStream(pdfAssinado);
PdfSigner signer = new PdfSigner(reader,os,true);
PdfDocument doc = signer.getDocument();
并删除后面的writer.close()
指令。
这将减少那些额外对象所需的内存使用量。
您可以像这样实例化PdfSigner
:
PdfSigner signer = new PdfSigner(reader,os,true);
如JavaDocs中所述,此构造函数将中间文件副本(在签名创建期间需要)保留在ByteArrayOutputStream
实例中,即在内存中:
/**
* Creates a PdfSigner instance. Uses a {@link java.io.ByteArrayOutputStream} instead of a temporary file.
*
* @param reader PdfReader that reads the PDF file
* @param outputStream OutputStream to write the signed PDF file
* @param append boolean to indicate whether the signing should happen in append mode or not
* @throws IOException
* @deprecated will be removed in next major release.
* Use {@link #PdfSigner(PdfReader, OutputStream, StampingProperties)} instead.
*/
@Deprecated
public PdfSigner(PdfReader reader, OutputStream outputStream, boolean append) throws IOException
为此,请在文件系统中提供一个临时文件:
String temporaryFile = pdfAssinado.getAbsolutePath() + ".tmp";
PdfSigner signer = new PdfSigner(reader, os, temporaryFile, true);
对于您的 500MB文件,这将再次减少500 MB的内存使用量。
以上更改将在对500 MB文件签名时减少1 GB以上的内存。我不知道这是否足够,但这至少可以大大减少内存需求。