尝试在 Java 中生成非常大的 PDF 文件时出现 ***Error

Posted

技术标签:

【中文标题】尝试在 Java 中生成非常大的 PDF 文件时出现 ***Error【英文标题】:I get a ***Error when trying to generate a very large PDF file in Java 【发布时间】:2021-01-12 19:18:43 【问题描述】:

当生成 PDF 的数据很小时,生成 PDF 时没有问题,但是当数据很大时,我得到 ***Error。

16:40:37,510 WARN  [org.hibernate.engine.loading.internal.LoadContexts] (default task-54) HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@5df3d7ab<rs=oracle.jdbc.driver.OracleResultSetImpl@357156df>
16:40:37,510 WARN  [org.hibernate.engine.loading.internal.CollectionLoadContext] (default task-54) HHH000160: On CollectionLoadContext#cleanup, localLoadingCollectionKeys contained [1] entries
16:40:37,528 ERROR [br.gov.ans.commons.rest.exception.handler.ExceptionHandler] (default task-54) org.modelmapper.MappingException: ModelMapper mapping errors:

1) Error mapping br.gov.ans.rps.rede.services.entities.Movimentacao to br.gov.ans.rps.rede.services.dtos.MovimentacaoDTO

1 error: org.modelmapper.MappingException: ModelMapper mapping errors:

1) Error mapping br.gov.ans.rps.rede.services.entities.Movimentacao to br.gov.ans.rps.rede.services.dtos.MovimentacaoDTO

1 error
    at org.modelmapper.internal.Errors.throwMappingExceptionIfErrorsExist(Errors.java:374)
    at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:69)
    at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:497)
    at org.modelmapper.ModelMapper.map(ModelMapper.java:340)
    at br.gov.ans.rps.rede.services.business.SolicitacaoNegocio.converterMovimentacaoVO(SolicitacaoNegocio.java:394)

io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:264) 在 io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81) 在 io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:175) 在 io.undertow.server.Connectors.executeRootHandler(Connectors.java:207) 在 io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:802) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 在 java.lang.Thread.run(Thread.java:745) 引起:java.lang.***Error 在 java.security.AccessController.doPrivileged(本机方法) 在 org.jboss.logmanager.formatters.Formatters$14.renderRaw(Formatters.java:638)

solicitacaoBean

public StreamedContent imprimirSolicitacao(SolicitacaoVO solicitacaoVO)
        
        StreamedContent download = new DefaultStreamedContent();

        try 
            SolicitacaoVO solicitacaoCompleta = solicitacaoClient.buscarSolicitacao(solicitacaoVO.getId().toString(), true);
                        
            ModelMapper modelMapper = new ModelMapper();
            
            SolicitacaoVO solicitacaoImprimir = modelMapper.map(solicitacaoCompleta, SolicitacaoVO.class);
            
            for(MovimentacaoVO mov:solicitacaoImprimir.getListaMovimentacao())
                
                List<PlanoVO> planosSemDuplicados = new ArrayList<PlanoVO>();
                mov.setListaPrestadoresExclusao(new ArrayList<PrestadorVO>());
                for (PlanoVO planoVO : mov.getListaPlanos()) 
                    if(!planosSemDuplicados.contains(planoVO))
                        planosSemDuplicados.add(planoVO);
                    
                    if(!mov.getListaPrestadoresExclusao().contains(planoVO.getPrestador()))
                        mov.getListaPrestadoresExclusao().add(planoVO.getPrestador());                              
                    
                
                mov.setListaPlanos(planosSemDuplicados);
                
                if ( mov.getReconsideracao()!=null && !mov.getReconsideracao().getArquivos().isEmpty() )
                    
                    int sizeList = mov.getReconsideracao().getArquivos().size();
                    int i = 1;
                    StringBuffer aux = new StringBuffer();
                    for ( ArquivoReconsideracaoVO arq : mov.getReconsideracao().getArquivos() )
                        aux.append(arq.getNomeArquivo());
                        if ( i < sizeList )
                            aux.append(";  ");
                        else 
                            aux.append(".");
                        
                        i++;
                    
                    mov.getReconsideracao().setNomeArquivosConcatenados(aux.toString());
                
                
                Collections.sort(mov.getListaPrestadoresExclusao());
                Collections.sort(mov.getListaPrestadores());
            
            
            InputStream pdfGerado = gerarRelatoriosolicitacaoOuMovimentacao(solicitacaoImprimir);
            download = prepararDownload(pdfGerado, "Solicitação", TipoArquivo.PDF.getContentType());
         catch (Exception e) 
            exibirMensagemErro("erro.imprimir.pdf");
            logger.error(e,e);
            return null;
        
        return download;
    

solicitacaoClient

@JsonIgnoreProperties(ignoreUnknown = true)
public class SolicitacaoClient implements Serializable
    private static final long serialVersionUID = 1L;

    @Inject
    @PropertiesInfo(file="services.properties", key="rps.rede.services.rest.uri")
    @Server
    private String uri;
    
    @Inject
    @Autenticado(value = "rps.rede", type = AuthType.BEARER)
    private Client cliente;
        
    @Inject
    Logger logger;
    
    public List<OperadoraVO> consultarOperadoras(String filtroCodigo, String filtroCNPJ, String filtroNome) throws Exception
        Response response = cliente.target(uri + "operadoras")
                .queryParam("codigo", filtroCodigo)
                .queryParam("cnpj", filtroCNPJ)
                .queryParam("nome", filtroNome)
                .request().get();
        try 
                if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL)
                        throw new Exception(response.readEntity(ErrorMessage.class).getError());
                     else
                        String json = (String) response.readEntity(String.class);
                        ObjectMapper mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
                        List<OperadoraVO> operadoras = mapper.readValue(json, new TypeReference<List<OperadoraVO>>()  );
                        
                        return operadoras;
                    
         finally 
            response.close();
        

    
    
    public SolicitacaoVO buscarSolicitacao(String id, boolean lgCompleto) throws Exception
        WebTarget target = cliente.target(uri + "/solicitacoes/" + id);
        
        target = target.queryParam("lg-completa", lgCompleto ? 1 : 0);
        
        Response response = target.request().get();
        
        try 
            if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL)
                throw new Exception(response.readEntity(ErrorMessage.class).getError());
             else
                String json = (String) response.readEntity(String.class);
                ObjectMapper mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
                return mapper.readValue(json, SolicitacaoVO.class);
            
         finally 
            response.close();
        
    
    
    public List<SolicitacaoVO> buscarSolicitacoes(String coOperadora, FiltroSolicitacao filtro) throws Exception
        
        Response response = cliente.target(uri + "operadoras/" + coOperadora + "/solicitacoes")
            .queryParam("cdSituacao", filtro.getStatus())
            .queryParam("dataEnvioInicio", Utils.dateToString(filtro.getDataInicio(), "dd/MM/yyyy"))
            .queryParam("dataEnvioFim", Utils.dateToString(filtro.getDataFim(), "dd/MM/yyyy HH:mm"))
            .queryParam("protocolo", Utils.getSomenteNumeros(filtro.getProtocolo()))
            .queryParam("gru", filtro.getGru())
            .request().get();

        try 
            if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL)
                throw new Exception(response.readEntity(ErrorMessage.class).getError());
             else
                String json = (String) response.readEntity(String.class);
                ObjectMapper mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
                return mapper.readValue(json, new TypeReference<List<SolicitacaoVO>>()  );
            
         finally 
            response.close();
        
    
    
    public ComprovanteRelatorio montarComprovanteRelatorio(Integer idSolicitacao) throws Exception
        
        Response response = cliente.target(uri + "/solicitacoes/" + idSolicitacao + "/montarComprovanteRelatorio").request().get();
        
        try 
            if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL)
                throw new Exception(response.readEntity(ErrorMessage.class).getError());
             else
                String json = (String) response.readEntity(String.class);
                ObjectMapper mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
                return mapper.readValue(json, ComprovanteRelatorio.class);
                       
         finally 
            response.close();
        
    

    public List<MovimentacaoRelatorioVO> montarRelatorioMovimentacoes(Integer idSolicitacao) throws Exception 
        
        Response response = cliente.target(uri + "/solicitacoes/" + idSolicitacao + "/relatorioMovimentacoes").request().get();

        gerarLogResponse(uri + "/solicitacoes/" + idSolicitacao + "/relatorioMovimentacoes", response);

        try 
            if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL)
                throw new Exception(response.readEntity(ErrorMessage.class).getError());
             else
                String json = (String) response.readEntity(String.class);
                ObjectMapper mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
                return mapper.readValue(json, new TypeReference<List<MovimentacaoRelatorioVO>>()  );
            
         finally 
            response.close();
        
    

    public List<MovimentacaoRelatorioVO> montarRelatorioConsultaMovimentacoesInterno(List<MovimentacaoVO> listaMovimentacoes) throws Exception 
        
        ConsultaMovimentacaoRelatorioVO consultaMovimentacaoRelatorioVO = new ConsultaMovimentacaoRelatorioVO();
        consultaMovimentacaoRelatorioVO.setListaMovimentacao(listaMovimentacoes);
        
        Response response = cliente.target(uri + "/solicitacoes/movimentacoes/relatorio/interno")
                .request().post(Entity.entity(consultaMovimentacaoRelatorioVO, MediaType.APPLICATION_JSON));
        
        try 
            if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL)
                throw new Exception(response.readEntity(ErrorMessage.class).getError());
             else
                String json = (String) response.readEntity(String.class);
                ObjectMapper mapper = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
                return mapper.readValue(json, new TypeReference<List<MovimentacaoRelatorioVO>>()  );
                       
         finally 
            response.close();
        
    
    
    private void gerarLogResponse(String url, Response response) 
        logger.info("URL chamada: " + url);
        logger.info("Header da resposta:");
        
        for (Entry<String, List<Object>> iterable_element : response.getHeaders().entrySet()) 
            for (Object iterable : iterable_element.getValue()) 
                logger.info(iterable_element.getKey() + " - " + iterable.toString());               
            
        
    

PDF 生成器 JasperReport

public class ReportHelper 
    
    @Inject
    private static Logger LOGGER;

    public enum FORMATO_RELATORIO 
        XLS, PDF;
    

    private static String gerarIdReport() 
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("ddMMyyyyhhmmss");
        String id = simpleDateFormat.format(new Date());

        return id;
    

    /**
     * Retorna caminho onde os relatórios (.jasper e .jrxml e tmps) ficam
     * armazenados
     * */
    private static String reportSourcePath() 

        return FacesContext.getCurrentInstance().getExternalContext().getRealPath("/jasper/") + "/";
    

    /**
     * Retorna o caminho onde os relatórios finais ficam no servidor (ex: .PDF)
     * */
    private String reportFile() 
        return FacesContext.getCurrentInstance().getExternalContext().getInitParameter("reportDirectory");
    

    /**
     * Abrir Janela com Arquivo (PDF, XLS, TXT e etc)
     * */
    public void abrirPoupUp(String fileName) 
        abrirPoupUp(fileName, null);
    

    public void abrirPoupUp(String fileName, String nomeJanela)
        HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
        String contextPath = req.getContextPath().replace("/", "");
        if (nomeJanela == null)
            nomeJanela = "Relatório";
        

        RequestContext.getCurrentInstance().execute(
            "window.open(/" + contextPath + "/jasper/" + fileName + ",'"+nomeJanela+"','width=800px,height=800px')");
        

    /**
     * Gera o relatório e retorna o nome do relatório gerado
     * */
    private static InputStream gerarRelatorio(String relatorio, List<Object> beans, Map<String, Object> params, FORMATO_RELATORIO formatoExportacao) 
        try 
            if (params == null) 
                params = new HashMap<String, Object>();
            

            JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(beans);
            
            DefaultJasperReportsContext.getInstance().setProperty("net.sf.jasperreports.components.sort.up.arrow.char", "");
            DefaultJasperReportsContext.getInstance().setProperty("net.sf.jasperreports.components.sort.down.arrow.char", "");

            String relatorioFormated = relatorio.endsWith(".jasper") ? relatorio : (new StringBuilder()).append(relatorio).append(".jasper").toString();
            net.sf.jasperreports.engine.JasperPrint jasperPrint = null;
            
            
            if (beans != null && beans.size() > 0) 
                jasperPrint = JasperFillManager.fillReport(reportSourcePath() + relatorioFormated, params, beanCollectionDataSource);
             else 
                jasperPrint = JasperFillManager.fillReport(reportSourcePath() + relatorioFormated, params, new JREmptyDataSource());
            

            StringBuilder nomeRelatorio = new StringBuilder();
            nomeRelatorio.append(relatorio + "_" + gerarIdReport());

            /*
             * JRExporter foi descontinuada. Utilizando JRPdfExporter e JRXlsExporter para tratar PDF e XLS separadamente.
             * -- Ricardo --
             */
            //JRExporter exporter = null;
            JRPdfExporter exporter_pdf = null;
            JRXlsExporter exporter_xls = null;

            ByteArrayOutputStream stream = new ByteArrayOutputStream();
                        
            if (formatoExportacao.equals(FORMATO_RELATORIO.PDF)) 
                exporter_pdf = new JRPdfExporter();
                nomeRelatorio.append(".pdf");
                
                exporter_pdf.setExporterInput(new SimpleExporterInput(jasperPrint));
                exporter_pdf.setExporterOutput(new SimpleOutputStreamExporterOutput(stream));
                exporter_pdf.exportReport();
            
                
            if (formatoExportacao.equals(FORMATO_RELATORIO.XLS)) 
                exporter_xls = new JRXlsExporter();
                nomeRelatorio.append(".xls");
                
                exporter_xls.setExporterInput(new SimpleExporterInput(jasperPrint));
                exporter_xls.setExporterOutput(new SimpleOutputStreamExporterOutput(stream));
                exporter_xls.exportReport();
            

            //ByteArrayOutputStream stream = new ByteArrayOutputStream();
            //exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
            //exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, stream);
            //exporter.exportReport();          

            return new ByteArrayInputStream(stream.toByteArray());
            
         catch (Exception e) 
            LOGGER.error(e, e);
            return null;
        
    

    /**
     * Geração de Relatório em XLS
     */
    public static InputStream gerarRelatorioXLS(String relatorio, List<Object> beans, Map<String, Object> params) 

        return gerarRelatorio(relatorio, beans, params, FORMATO_RELATORIO.XLS);

    

    /**
     * Padrão para Arquivos PDF
     * @throws FileNotFoundException 
     * */
    public static InputStream gerarRelatorioPDF(String relatorio, Object object) throws FileNotFoundException 
        List<Object> beans = new ArrayList<Object>();
        beans.add(object);
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("SUBREPORT_DIR", reportSourcePath());
        params.put("LOGO",new FileInputStream(FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/images/logo-ans-sem-slogan.png")));

        return gerarRelatorio(relatorio, beans, params, FORMATO_RELATORIO.PDF);

    


【问题讨论】:

【参考方案1】:

我不知道你的数据模型,但也许你在对象模型中有一个循环,即 obj1 引用 obj2 又引用 obj1?一个简单的模型映射会导致无限递归,从而导致堆栈溢出。也许 obj2 的对象映射器应该使用只映射 obj1 子集的映射器,以避免递归。

【讨论】:

这种情况下总是会报错,但是数据量少的时候不会报错。 怎么会总是发生?这本质上是数据相关的。也许您的“小”数据集没有循环?也可能是您刚刚达到线程堆栈限制并且结构实际上是合法的。如果是这样,请尝试使用 -Xss 设置增加线程堆栈大小,例如 -Xss1M 我的 eclipse.ini 文件:-Dosgi.requiredJavaVersion=1.8 -Dosgi.instance.area.default=@user.home/eclipse-workspace -XX:+UseG1GC -XX:+UseStringDeduplication --add -modules=ALL-SYSTEM -Dosgi.requiredJavaVersion=1.8 -Xms1024m -Xmx2048m --add-modules=ALL-SYSTEM 我需要放-Xss1M吗? 我认为您的应用程序不会在 eclipse JVM 中运行,因此您可能需要调整应用程序的运行配置。

以上是关于尝试在 Java 中生成非常大的 PDF 文件时出现 ***Error的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 中生成第一个 PDF 页面的图像

如何在 node.js 中生成 PDF

在ios中生成PDF文件[重复]

使用 REST API 在 excel 或 pdf 中生成大数据

未在 laravel 中生成 PDF

在 Eclipse 中生成 APK 时出现“未找到 DEX 文件”错误