最近公司需要将前端一个图表统计导出为pdf。前端导出显示的pdf还是可以的,但是将会导致页面不可用与卡死状态。所以由后端寻找解决方案。
以下为解决方案调研
https://www.cnblogs.com/IT-study/p/13706690.html
由于自己开发一个公共的导出pdf功能比较费时费力,而且导出过程中也遇到了各种样式问题。考虑再三决定使用第三方导出来解决这个问题。
好处:接入简单给个H5url即可,无需要考虑导出中遇到的写出的样式与排版问题
坏处:无法做到完全自定义。拿restPack举例子 ,导出长图无法自定义宽度,目前导出有1280px宽度,业务需要手机预览宽度只要 A6纸的规格即可。也可能可以通过js、css参数去解决,我没找到对应的方法。
解决思路
1.将一个可访问的H5URL转换为Pdf文件 2.将Pdf文件写到页面
H5URL转换为Pdf文件
1 @Component 2 public class H5UrlExportPdf { 3 4 5 @Value("${restpack.token}") 6 private String token = "HA5jLSVtSnjl3fmlhPgTg7rPRE4OnywDvh1CrbanXmpBvNqA"; 7 8 9 /** 10 * https://restpack.io/html2pdf/docs 11 */ 12 public ReturnRestPackPdf exportPdf(RestPackPdfParameters restpackPdfParameters) throws IOException { 13 14 15 ReturnRestPackPdf returnRestPackPdf; 16 17 //请求URL 18 String reqUrl = "https://restpack.io/api/html2pdf/v6/convert"; 19 StringBuilder stringBuilder = new StringBuilder(); 20 21 //地址需要encode 22 String firstP = "url"; 23 stringBuilder.append(firstP).append("=").append(restpackPdfParameters.getUrl()); 24 25 Map<String, String> describe = BeanHelper.describe(restpackPdfParameters); 26 describe.forEach((name, val) -> { 27 28 if (!firstP.equalsIgnoreCase(name)) { 29 stringBuilder.append("&").append(name).append("=").append(val); 30 } 31 }); 32 33 System.out.println(stringBuilder.toString()); 34 35 byte[] postData = stringBuilder.toString().getBytes(StandardCharsets.UTF_8); 36 HttpURLConnection con = null; 37 38 try { 39 40 URL myUrl = new URL(reqUrl); 41 con = (HttpURLConnection) myUrl.openConnection(); 42 43 con.setDoOutput(true); 44 con.setRequestMethod("POST"); 45 con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 46 con.setRequestProperty("x-access-token", token); 47 48 try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) { 49 wr.write(postData); 50 } 51 52 StringBuilder content; 53 54 try (BufferedReader in = new BufferedReader( 55 new InputStreamReader(con.getInputStream()))) { 56 57 String line; 58 content = new StringBuilder(); 59 60 while ((line = in.readLine()) != null) { 61 content.append(line); 62 content.append(System.lineSeparator()); 63 } 64 } 65 66 returnRestPackPdf = JSONObject.parseObject(content.toString(), ReturnRestPackPdf.class); 67 68 } finally { 69 if (con != null) { 70 con.disconnect(); 71 } 72 } 73 74 return returnRestPackPdf; 75 } 76 77 78 }
H5URL导出文档入参
1 @Data 2 public class RestPackPdfParameters implements Serializable{ 3 4 private static final long serialVersionUID = 1L; 5 6 7 /** 8 * 需要导出文档H5URL 9 * The URL of web page, including the protocol that you want to capture. 10 * Example: http://example.com 11 */ 12 private String url; 13 14 15 /** 16 * Return a JSON response with the resulting image's URL instead of the image itself. 17 * Default: false 18 */ 19 private Boolean json = true; 20 21 22 /** 23 * Page size for created document 24 * 25 * Default: Full 26 * Pattern: A0 | A1 | A2 | A3 | A4 | A5 | A6 | Legal | Letter | Tabloid | Ledger | Full 27 * 28 * Legal:width 816 多张图片阶段 29 * Letter:width 816 多张图片阶段 30 * Tabloid:width 1056 多张图片阶段 31 * Ledger:width 1632 多张图片阶段 32 * Full:width 1280 一张图片 33 */ 34 private String pdf_page="Full"; 35 36 public RestPackPdfParameters() { 37 } 38 39 public RestPackPdfParameters(String url) { 40 this.url = url; 41 } 42 }
H5URL导出文档返回参数
1 @Data 2 public class ReturnRestPackPdf implements Serializable{ 3 4 private static final long serialVersionUID = 1L; 5 6 7 private Boolean cached; 8 private String content_type; 9 /** pdf文件类型地址 */ 10 private String file; 11 private Integer height; 12 private Integer width; 13 /** pdf图片类型地址 */ 14 private String image; 15 private String length; 16 private String remote_status; 17 private String run_time; 18 private String url; 19 20 }
将文件写到页面
1 private void downFileHtml(String name, @RequestParam(value = "fileUrl", required = false) String fileUrl, HttpServletResponse response, Boolean useName) { 2 BufferedInputStream bis = null; 3 BufferedOutputStream bos = null; 4 OutputStream output = null; 5 try { 6 log.info("downLoadFileStart:" + fileUrl); 7 response.setContentType("application/octet-stream; charset=UTF-8"); 8 if (useName) { 9 10 } else { 11 String ext = fileUrl.substring(fileUrl.lastIndexOf(".")); 12 name = name + ext; 13 name = URLDecoder.decode(name, "UTF-8"); 14 } 15 16 response.setHeader("Content-Disposition", "attachment;fileName="" + new String(name.getBytes("GBK"), "ISO8859-1") + """); 17 18 URL url = new URL(fileUrl); 19 bis = new BufferedInputStream(url.openStream()); 20 output = response.getOutputStream(); 21 bos = new BufferedOutputStream(output); 22 log.info("downLoadFileCopyStream:" + fileUrl); 23 byte[] buff = new byte[2048]; 24 int bytesRead; 25 while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) { 26 log.info("downLoadFileCopyStreamDetail:" + bytesRead); 27 bos.write(buff, 0, bytesRead); 28 } 29 log.info("downLoadFileEnd:" + fileUrl); 30 } catch (Exception e) { 31 log.error("downLoadFileError:" + fileUrl + ":error:" + e.getMessage()); 32 e.printStackTrace(); 33 } finally { 34 try { 35 if (bos != null) { 36 bos.flush(); 37 bos.close(); 38 } 39 if (bis != null) { 40 bis.close(); 41 } 42 if (output != null) { 43 output.close(); 44 } 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 } 49 }
使用事项
1.url 为转换pdf的页面地址,改地址必须能直接访问(无登录功能) 2.pdf_page 有几个页面规格 A0 | A1 | A2 | A3 | A4 | A5 | A6 | Legal | Letter | Tabloid | Ledger | Full 。Full可导出一张长图,但是宽度不可定。其他的规格与A4类似,导出的是多张图片。 3.pdf_width 、pdf_height 可自定义页面规格。一旦pdf_page设置,pdf_width pdf_height必须为空。 4.一旦付费成功 css、js 可进行调节生成pdf文件产生的样式问题。 5.我们需要导出一整张图片,但是一整张图的宽度不可自定义。导出的宽度为1280px,手机上显示只需要800px。目前这个问题还不知道怎么解决