使用wkhtmltopdf生成pdf并下载pdf

时间:2015-10-15 10:24:54

标签: java spring jsp wkhtmltopdf

我正在一个旧项目中工作。该项目在Spring MVC中。在项目中,我必须从jsp页面生成pdf文件并存储在某个位置并下载该文件。为此,我使用wkhtmltopdf工具将一个特定的jsp页面转换为pdf格式。使用wkhtmltopdf有时工作正常,它会在特定位置生成pdf,但有时需要更多时间。此外,当我尝试从特定位置下载文件时,有时它会下载0KB大小的文件,或者有时下载的文件无法打开(有一些大小),但有时下载完美。如果我在定义位置检查文件,它会存在并正常打开。 这是我在控制器类中的代码。

@RequestMapping(value="/dwn.htm",method=RequestMethod.GET)
public void dwAppFm(HttpSession session,HttpServletRequest request,HttpServletResponse response,@RequestParam String id) throws IOException,InterruptedException
{           
         final int BUFFER_SIZES=4096;
         ServletContext context=request.getServletContext();             
         String savePath="/tmp/";//PDF file Generate Path
         String fileName="PDFFileName"; //Pdf file name
         FileInputStream inputStream=null;
         BufferedInputStream bufferedInputStream=null;
         OutputStream outputStream=null;
             printApp(id,fileName);
         Thread.sleep(1000);
         printApp(id,fileName);         
         File download=new File(savePath+fileName+".pdf");
         while(!download.canRead())
           {
                Thread.sleep(1000);
            printApp(id,fileName);
            download=new File(savePath+fileName+".pdf");                                    
           }

       if(download.canRead()){//if the file can read
        try{
            Thread.sleep(1000);
            inputStream=new FileInputStream(download);
                bufferedInputStream=new BufferedInputStream(inputStream);
            String mimeType = context.getMimeType(savePath+fileName+".pdf");
            if (mimeType == null) {                 
                mimeType = "application/octet-stream";
            }
            System.out.println("MIME type: " + mimeType);
            response.setContentType(mimeType);
            response.setContentLength((int)download.length());              
            String headerKey="Content-Disposition";
            String headerValue=String.format("attachment;filename=\"%s\"", download.getName());
            response.setHeader(headerKey, headerValue);             
            outputStream=response.getOutputStream();
            byte[] buffer=new byte[BUFFER_SIZES];
            int bytesRead=-1;

            while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }           


        }catch(Exception e)
        {               
            e.printStackTrace();

        }

        finally
        {
            try{                    
                if(inputStream!=null)inputStream.close();
                if(bufferedInputStream!=null)bufferedInputStream.close();
                if(outputStream!=null)outputStream.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
       }    

}

public void printApp(String id,String fileName)
{
    try{
        String urlPath="http://localhost:8080/proj";
        urlPath+="/genApp.htm?id="+id;//generate url to execute wkhtmltopdf 
        String wxpath="/home/exm/wkhtmltopdf";//the path where wkhtmltopdf located  
        String save="/tmp/"+fileName+".pdf";//File save Pathname
        Process process=null;
        process=Runtime.getRuntime().exec(wxpath+" "+urlPath+" "+save);     
        }catch(Exception e)
        {}
}

@RequestMapping(value="/genApp.htm",method=RequestMethod.GET)
public String getApplicationPDF(HttpServletRequest request,HttpSession session,@RequestParam String id)
{   
    UDets uDets=uService.getAllById(Long.parseLong(id));//Methods to get details
    request.setAttribute("uDets",uDets );   
    return "makeApp";//Name of the jsp page
}

在我的代码中我使用了Thread.sleep(1000)和printApp(id,fileName)方法三次,因为有时wkhtmltopdf无法在一定时间内生成pdf,然后下载0KB文件的概率更高。我没有共享jsp页面,因为jsp页面包含许多行的简单jsp页面代码(生成的pdf文件的大小是两页)。

所以问题是我应该在代码中进行哪些更改,以便生成并下载pdf文件,同时服务器负载也很重。

如果有任何最佳程序或想法,请分享。

我不喜欢使用itext,因为jsp页面包含复杂的设计。任何建议也很明显,也要提前感谢。

1 个答案:

答案 0 :(得分:0)

我会说你的代码存在缺陷,不仅仅是一点点,而是很大的时间。您正在检查是否可以读取文件,如果不是,则重新开始写入同一文件的过程(至少两次)。在某些时候,您将最终尝试写入同一文件的多个进程,从而导致奇怪的行为。

我会重构printApp方法以返回它创建的Process。然后在该流程上调用waitFor。如果它返回0并且没有被中断,则表示已成功完成,您应该能够下载该文件。

@RequestMapping(value="/dwn.htm",method=RequestMethod.GET)
public void dwAppFm(HttpSession session,HttpServletRequest request,HttpServletResponse response,@RequestParam String id) throws IOException,InterruptedException
{           
         String savePath="/tmp/";//PDF file Generate Path
         String fileName="PDFFileName.pdf"; //Pdf file name
         File download = new File(savePath, fileName);

         try {
           Process process = printApp(id, download.getPath());
           int status = process.waitFor();
           if (status == 0) {
             response.setContentType("application/pdf");
             response.setContentLength((int)download.length());              
             String headerKey="Content-Disposition";
             String headerValue=String.format("attachment;filename=\"%s\"", download.getName());
             StreamUtils.copy(new FileInputStream(download), response.getOutputStream())
           } else {
             // do something if it fails.
           }

         } catch (IOException ioe) {
           // Do something to handle exception
         } catch (InterruptedException ie) {
           // Do something to handle exception
         }

       }    

}

public Process printApp(String id, String pdf) throws IOException {    
        String urlPath="http://localhost:8080/proj";
        urlPath+="/genApp.htm?id="+id;//generate url to execute wkhtmltopdf 
        String wxpath="/home/exm/wkhtmltopdf";//the path where wkhtmltopdf located  
        String command = wxpath+" "+urlPath+" "+pdf;        
        return Runtime.getRuntime().exec(command);     
}

上面的代码应该是诀窍。