使用 wkhtmltopdf 生成 pdf 并下载 pdf

Generating pdf with wkhtmltopdf and download the pdf

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

@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) 方法 3 次,因为有时 wkhtmltopdf 无法在特定时间内生成 pdf,然后下载 0KB 文件的可能性更大。我没有分享 jsp 页面,因为 jsp 页面包含很多行的简单 jsp 页面代码(生成的 pdf 文件的大小是两页)。

所以问题是我应该在我的代码中更改什么,以便生成和下载 pdf 文件而不会在服务器的重负载下出现故障。

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

我不喜欢使用 itext,因为 jsp 页面包含复杂的设计。任何建议也很重要,也在此先感谢。

我会说你的代码有缺陷,不仅是一点点,而且是很大的。您正在检查是否可以读取文件,如果不能,您将再次开始写入同一文件的过程(至少两次)。有时您会遇到多个进程试图写入同一个文件,从而导致奇怪的行为。

我会将 printApp 方法重构为 return 它创建的 Process 方法。然后在该进程上调用 waitFor。如果它 returns 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);     
}

类似于上面的代码应该可以解决问题。