Java Heap Space عند تسجيل ملف Pdf باستخدام IText 7

0

أقوم بوضع هذا الأسلوب للتوقيع على ملف 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) ولكن عندما أحاول التوقيع على ملف بحجم 500 ميجابايت ، حصلت على Java Heap Space في الصف:

signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, 0, PdfSigner.CryptoStandard.CMS);

إذا حاولت التوقيع على عمل ملفات أصغر (حاولت فقط واحدًا تلو الآخر ، فلا أعرف إذا حاولت أكثر من واحد في نفس الوقت فسوف أتلقى نفس الخطأ)

أحاول بالفعل تغيير الذاكرة من تطبيقي دون نجاح.

1 إجابة

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 جميع البيانات من هذا الدفق إلى byte[] لدعم الوصول العشوائي المناسب.

إذا سمحت لـ iText بدلاً من ذلك برؤية أن المصدر هو ملف نظام ملفات محلي ، فيمكنه استخدام الوصول العشوائي للملف ولا يقوم بإنشاء نسخة داخل الملف من الذاكرة. استخدم ببساطة

PdfReader reader = new PdfReader(pdfOriginal);

بالنسبة لملف 500 ميجابايت ، سيقلل هذا من استخدام الذاكرة بمقدار 500 ميجابايت بالإضافة إلى القليل بالفعل.

إضافي 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 الحالات.

وبالتالي ، لا تقم بإنشاء الخاصة بك 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);

بالنسبة لملف 500 ميجابايت ، سيقلل هذا مرة أخرى استخدام الذاكرة بمقدار 500 ميجابايت بالإضافة إلى القليل.


ستقلل التغييرات المذكورة أعلاه بصمة الذاكرة الخاصة بك بأكثر من 1 جيجابايت عند توقيع ملف 500 ميجابايت. لا أعرف ما إذا كان ذلك كافياً ، لكن هذا على الأقل سيقلل من متطلبات الذاكرة بشكل كبير.

:مؤلف
فوق
قائمة طعام