Procházet zdrojové kódy

预览时先生成第一页pdf,返回给前台展示,再接着生成后续pdf,以提高预览速度;异常处理统一采用修改后的SystemError

sunyj před 9 roky
rodič
revize
d288d1c936

+ 29 - 32
src/main/java/com/uas/report/controller/PrintController.java

@@ -72,8 +72,7 @@ public class PrintController {
 			try {
 				request.getRequestDispatcher("preview2").forward(request, response);
 			} catch (IOException | ServletException e) {
-				e.printStackTrace();
-				throw new SystemError(e.getMessage());
+				throw new SystemError(e);
 			}
 		}
 		// 下载pdf、纯数据excel
@@ -101,7 +100,6 @@ public class PrintController {
 	 * @param exportFileType
 	 *            报表导出的格式,默认为pdf
 	 * @param response
-	 * @return
 	 */
 	@RequestMapping("/export")
 	@ResponseBody
@@ -127,11 +125,8 @@ public class PrintController {
 		// 文件无效(不存在或过期),创建
 		if (!printService.isFileValid(file.getPath())) {
 			data = printService.export(userName, reportName, whereCondition, otherParameters, exportFileType);
-			String message = "";
 			if (ArrayUtils.isEmpty(data)) {
-				message = "报表导出失败:" + reportName;
-				logger.error(message);
-				throw new SystemError(message);
+				throw new SystemError("报表导出失败:" + reportName);
 			}
 			printService.writeDataToFile(file.getPath(), data);
 		} else {
@@ -141,15 +136,13 @@ public class PrintController {
 				data = new byte[fileInputStream.available()];
 				fileInputStream.read(data);
 			} catch (IOException e) {
-				e.printStackTrace();
-				throw new SystemError(e.getMessage());
+				throw new SystemError(e);
 			} finally {
 				if (fileInputStream != null) {
 					try {
 						fileInputStream.close();
 					} catch (IOException e) {
-						e.printStackTrace();
-						throw new SystemError(e.getMessage());
+						throw new SystemError(e);
 					}
 				}
 			}
@@ -187,7 +180,8 @@ public class PrintController {
 	 *            JSON格式,数据为键值对
 	 * @param pageIndex
 	 *            分页展示,当前页码,从0开始
-	 * @return
+	 * @param response
+	 * @return 包括pageSize、pdfPath
 	 */
 	@RequestMapping(value = "/loadPdfData")
 	@ResponseBody
@@ -203,20 +197,28 @@ public class PrintController {
 		File file = new File(PathUtils.getAppPath() + pdfPath);
 		// 文件无效(不存在或过期),重新创建pdf文件
 		if (!printService.isFileValid(file.getPath())) {
-			result = printService.preview(userName, reportName, whereCondition, otherParameters, pageIndex);
-			byte[] data = null;
-			if (result != null && result.containsKey("data")) {
-				data = (byte[]) result.remove("data");
-			}
-			String message = "";
-			if (data == null) {
-				message = "获取预览数据失败";
-				logger.error(message);
-				throw new SystemError(message);
+			// 若参数pageIndex不为null,表示是预览
+			// 检查第一页的pdf文件是否存在,若不存在,先生成第一页pdf,返回给前台展示,
+			// 再开线程生成后面页的pdf和总的pdf(即未分页的pdf),以备后续可能的打印、下载操作使用
+			if (pageIndex != null) {
+				Integer pageSize = printService.previewFirstPage(userName, reportName, whereCondition, otherParameters, file.getPath());
+				result=new HashMap<>();
+				result.put("pageSize", pageSize);
+			} 
+			//参数pageIndex为null,表示是直接打印,需要先生成总的pdf
+			else {
+				result = printService.preview(userName, reportName, whereCondition, otherParameters, null);
+				byte[] data = null;
+				if (result != null && result.containsKey("data")) {
+					data = (byte[]) result.remove("data");
+				}
+				if (data == null) {
+					throw new SystemError("获取预览数据失败");
+				}
+				printService.writeDataToFile(file.getPath(), data);
+				// 同时生成分页的pdf
+				printService.writePagedPdfFiles(file.getPath());
 			}
-			printService.writeDataToFile(file.getPath(), data);
-			// 同时生成分页的pdf
-			printService.writePagedPdfFiles(file.getPath());
 		} else {
 			result = new HashMap<>();
 			result.put("pageSize", printService.getPageSize(file.getPath()));
@@ -235,16 +237,11 @@ public class PrintController {
 	 *            报表名
 	 */
 	private void checkParameters(String userName, String reportName) {
-		String message = "";
 		if (StringUtils.isEmpty(userName)) {
-			message = "未传入当前账套用户名!";
-			logger.error(message);
-			throw new SystemError(message);
+			throw new SystemError("未传入当前账套用户名!");
 		}
 		if (StringUtils.isEmpty(reportName)) {
-			message = "未传入报表名称!";
-			logger.error(message);
-			throw new SystemError(message);
+			throw new SystemError("未传入报表名称!");
 		}
 	}
 }

+ 1 - 0
src/main/java/com/uas/report/core/advice/ExceptionHandlerAdvice.java

@@ -93,6 +93,7 @@ public class ExceptionHandlerAdvice {
 	 */
 	@ExceptionHandler(SystemError.class)
 	public ResponseEntity<ModelMap> handleSystemError(SystemError ex) {
+		logger.error(ex.getMessage());
 		HttpHeaders headers = new HttpHeaders();
 		headers.add("Content-Type", "application/json; charset=utf-8");
 		ModelMap map = new ModelMap();

+ 19 - 0
src/main/java/com/uas/report/core/exception/SystemError.java

@@ -15,6 +15,25 @@ public class SystemError extends RuntimeException {
 
 	private String message;
 
+	public SystemError(Throwable e) {
+		this.message = getMessage(e);
+	}
+
+	/**
+	 * 获取异常及其Cause拼接成的字符串
+	 * 
+	 * @param e
+	 *            异常
+	 * @return 拼接后的结果
+	 */
+	private String getMessage(Throwable e) {
+		StringBuilder sb = new StringBuilder(e.toString());
+		if (e.getCause() != null) {
+			sb.append("\nCaused by: ").append(getMessage(e.getCause()));
+		}
+		return sb.toString();
+	}
+
 	public String getMessage() {
 		return message;
 	}

+ 21 - 0
src/main/java/com/uas/report/service/PrintService.java

@@ -48,6 +48,27 @@ public interface PrintService {
 	public Map<String, Object> preview(String userName, String reportName, String whereCondition,
 			String otherParameters, Integer pageIndex);
 
+	/**
+	 * 先生成第一页pdf,返回给前台展示,以提高预览速度;
+	 * 
+	 * 再开线程生成后面页的pdf和总的pdf(即未分页的pdf),以备后续可能的打印、下载操作使用
+	 * 
+	 * @param userName
+	 *            不为null;当前账套用户名
+	 * @param reportName
+	 *            不为null;需要预览的报表的名称,不带任何后缀(如预览采购单,即为"Purchase")
+	 * @param whereCondition
+	 *            可为null;where之后的条件(包括where)
+	 * @param otherParameters
+	 *            若模板已指定需要的参数,则不可为null;其他参数,区别于whereCondition,报表某些字段的值取决于这些参数;
+	 *            JSON格式,数据为键值对
+	 * @param pdfFilePath
+	 *            该报表对应的pdf绝对路径
+	 * @return 总页数
+	 */
+	public Integer previewFirstPage(String userName, String reportName, String whereCondition, String otherParameters,
+			String pdfFilePath);
+
 	/**
 	 * 利用字节数组数据创建文件
 	 * 

+ 91 - 72
src/main/java/com/uas/report/service/impl/PrintServiceImpl.java

@@ -86,13 +86,64 @@ public class PrintServiceImpl implements PrintService {
 		return print(userName, reportName, whereCondition, otherParameters, null, pageIndex);
 	}
 
+	@Override
+	public Integer previewFirstPage(final String userName, final String reportName, final String whereCondition,
+			final String otherParameters, final String pdfFilePath) {
+		// 先生成第一页pdf
+		final Integer pageSize = writePdfData(userName, reportName, whereCondition, otherParameters,
+				pdfFilePath.replace(".pdf", "_1.pdf"), 1);
+
+		// 再开线程生成后面页的pdf和总的pdf(即未分页的pdf)
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+				// 生成之后页的pdf
+				for (int i = 2; i <= pageSize; i++) {
+					writePdfData(userName, reportName, whereCondition, otherParameters,
+							pdfFilePath.replace(".pdf", "_" + i + ".pdf"), i);
+				}
+				// 生成总的pdf
+				writePdfData(userName, reportName, whereCondition, otherParameters, pdfFilePath, null);
+			}
+		}).start();
+
+		return pageSize;
+	}
+
+	/**
+	 * 获取第pageIndex页的pdf数据并写入pdfFilePath路径下
+	 * 
+	 * @param userName
+	 * @param reportName
+	 * @param whereCondition
+	 * @param otherParameters
+	 * @param pdfFilePath
+	 * @param pageIndex
+	 * @return 总页数
+	 */
+	public Integer writePdfData(String userName, String reportName, String whereCondition, String otherParameters,
+			String pdfFilePath, Integer pageIndex) {
+		File file = new File(pdfFilePath);
+		Map<String, Object> result = preview(userName, reportName, whereCondition, otherParameters, pageIndex);
+		byte[] data = null;
+		Integer pageSize = null;
+		if (result != null && result.containsKey("data") && result.containsKey("pageSize")) {
+			data = (byte[]) result.remove("data");
+			pageSize = (Integer) result.remove("pageSize");
+		}
+		if (data == null || pageSize == null) {
+			throw new SystemError("获取预览数据失败");
+		}
+		writeDataToFile(file.getPath(), data);
+		return pageSize;
+	}
+
 	@Override
 	public void writeDataToFile(String filePath, byte[] data) {
 		File file = new File(filePath);
 		if (!file.getParentFile().exists()) {
 			file.getParentFile().mkdirs();
 		}
-
 		try {
 			FileOutputStream fos = new FileOutputStream(file);
 			fos.write(data);
@@ -100,8 +151,7 @@ public class PrintServiceImpl implements PrintService {
 			logger.info("Write file..." + file.getPath());
 			fos.close();
 		} catch (IOException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
+			throw new SystemError(e);
 		}
 	}
 
@@ -123,8 +173,7 @@ public class PrintServiceImpl implements PrintService {
 			}
 			pdfReader.close();
 		} catch (IOException | DocumentException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
+			throw new SystemError(e);
 		}
 	}
 
@@ -133,8 +182,7 @@ public class PrintServiceImpl implements PrintService {
 		try {
 			return new PdfReader(pdfFilePath).getNumberOfPages();
 		} catch (IOException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
+			throw new SystemError(e);
 		}
 	}
 
@@ -186,24 +234,19 @@ public class PrintServiceImpl implements PrintService {
 			String exportFileType, Integer pageIndex) {
 		try {
 			resourceService.syncResources(userName);
-		} catch (URISyntaxException | IOException e1) {
-			e1.printStackTrace();
-			throw new SystemError(e1.getMessage());
+		} catch (URISyntaxException | IOException e) {
+			throw new SystemError(e);
 		}
 
 		// 报表路径为报表根路径REPORT_DIR + 当前账套用户名userName
-		String reportDir = new StringBuilder(jsRestAPIConf.getLocalBaseDir()).append("/").append(userName).append("/")
-				.toString();
-
-		String jrxmlFilePath = new StringBuilder(reportDir).append("jrxml").append("/").append(reportName)
+		String reportDir = new StringBuilder(jsRestAPIConf.getLocalBaseDir()).append("/").append(userName).toString();
+		String jrxmlFilePath = new StringBuilder(reportDir).append("/").append("jrxml").append("/").append(reportName)
 				.append(".jrxml").toString();
 		File jrxmlFile = new File(jrxmlFilePath);
-		String message = "";
 		// 报表模板不存在
 		if (!jrxmlFile.exists()) {
-			message = "未发现模板文件:" + jrxmlFile.getPath();
-			logger.error(message);
-			throw new SystemError(message);
+			// 替换windows下路径中的双反斜杠为单斜杠
+			throw new SystemError("未发现模板文件:" + jrxmlFile.getPath().replaceAll("\\\\", "/"));
 		}
 
 		String jasperFilePath = jrxmlFile.getPath().replace(".jrxml", ".jasper");
@@ -245,9 +288,7 @@ public class PrintServiceImpl implements PrintService {
 			// 获取数据源
 			DataSource dataSource = getDataSource(userName);
 			if (dataSource == null) {
-				message = "获取数据源失败";
-				logger.error(message);
-				throw new SystemError(message);
+				throw new SystemError("获取数据源失败");
 			}
 
 			connection = dataSource.getConnection();
@@ -262,7 +303,7 @@ public class PrintServiceImpl implements PrintService {
 				// 只导出数据
 				if (exportFileType.equals("xls_with_only_data")) {
 					JasperDesign jasperDesign = JRXmlLoader.load(jrxmlFile);
-					// 移除多余元素
+					// 移除模板中多余元素
 					removeUnusedElements(jasperDesign);
 					exportFileType = "xls";
 					JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
@@ -272,25 +313,27 @@ public class PrintServiceImpl implements PrintService {
 					jasperPrint = JasperFillManager.fillReport(jasperFilePath, parameters, connection);
 				}
 
-				boolean exportSucceeded = exportReport(jasperPrint, exportFileType, outputStream);
-				if (exportSucceeded) {
-					byte[] data = outputStream.toByteArray();
-					outputStream.close();
-					result.put("data", data);
-					return result;
-				}
+				exportReport(jasperPrint, exportFileType, outputStream);
+				byte[] data = outputStream.toByteArray();
+				outputStream.close();
+				result.put("data", data);
+				return result;
 			}
 			// 报表预览,则直接输出pdf,并且需要进行分页
-			else if (pageIndex != null) {
-				// 页码并非有效数值,重置为第一页
+			else {
 				jasperPrint = JasperFillManager.fillReport(jasperFilePath, parameters, connection);
-				if (pageIndex < 0 || pageIndex >= jasperPrint.getPages().size()) {
-					pageIndex = 0;
-				}
 				JRPdfExporter exporter = new JRPdfExporter();
-				SimplePdfReportConfiguration configuration = new SimplePdfReportConfiguration();
-				configuration.setPageIndex(pageIndex);
-				// exporter.setConfiguration(configuration);
+				if (pageIndex != null) {
+					// 前端显示时页码从1开始,生成报表时页码从0开始
+					pageIndex -= 1;
+					// 页码并非有效数值,重置为第一页
+					if (pageIndex < 0 || pageIndex >= jasperPrint.getPages().size()) {
+						pageIndex = 0;
+					}
+					SimplePdfReportConfiguration configuration = new SimplePdfReportConfiguration();
+					configuration.setPageIndex(pageIndex);
+					exporter.setConfiguration(configuration);
+				}
 				ExporterInput exporterInput = new SimpleExporterInput(jasperPrint);
 				exporter.setExporterInput(exporterInput);
 				OutputStreamExporterOutput exporterOutput = new SimpleOutputStreamExporterOutput(outputStream);
@@ -302,32 +345,18 @@ public class PrintServiceImpl implements PrintService {
 				result.put("data", data);
 				result.put("pageSize", jasperPrint.getPages().size());
 				return result;
-			} else {
-				message = "exportFileType 和 pageIndex 不能同时为空!";
-				logger.error(message);
-				throw new SystemError(message);
 			}
-		} catch (SQLException e) {
-			message = "数据库连接错误!";
-			logger.error(message);
-			throw new SystemError(message);
-		} catch (JRException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
-		} catch (IOException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
+		} catch (SQLException | JRException | IOException e) {
+			throw new SystemError(e);
 		} finally {
 			if (connection != null) {
 				try {
 					connection.close();
 				} catch (SQLException e) {
-					e.printStackTrace();
-					throw new SystemError(e.getMessage());
+					throw new SystemError(e);
 				}
 			}
 		}
-		return null;
 	}
 
 	/**
@@ -372,12 +401,8 @@ public class PrintServiceImpl implements PrintService {
 					return DruidDataSourceFactory.createDataSource(properties);
 				}
 			}
-		} catch (SQLException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
 		} catch (Exception e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
+			throw new SystemError(e);
 		} finally {
 			try {
 				if (resultSet != null) {
@@ -385,7 +410,6 @@ public class PrintServiceImpl implements PrintService {
 				}
 			} catch (SQLException e) {
 				e.printStackTrace();
-				throw new SystemError(e.getMessage());
 			}
 			try {
 				if (preparedStatement != null) {
@@ -393,13 +417,13 @@ public class PrintServiceImpl implements PrintService {
 				}
 			} catch (SQLException e) {
 				e.printStackTrace();
-				throw new SystemError(e.getMessage());
 			}
 			try {
-				connection.close();
+				if (connection != null) {
+					connection.close();
+				}
 			} catch (SQLException e) {
-				e.printStackTrace();
-				throw new SystemError(e.getMessage());
+				throw new SystemError(e);
 			}
 		}
 		return null;
@@ -411,24 +435,19 @@ public class PrintServiceImpl implements PrintService {
 	 * @param jasperPrint
 	 * @param exportFileType
 	 * @param outputStream
-	 * @return 报表是否成功导出
 	 */
-	private boolean exportReport(JasperPrint jasperPrint, String exportFileType, OutputStream outputStream) {
+	private void exportReport(JasperPrint jasperPrint, String exportFileType, OutputStream outputStream) {
 		try {
 			if (exportFileType.equals("pdf")) {
 				exportReportToPdf(jasperPrint, outputStream);
 			} else if (exportFileType.equals("xls")) {
 				exportReportToXls(jasperPrint, outputStream);
 			} else {
-				logger.error("不支持导出为 " + exportFileType + "格式!");
-				return false;
+				throw new SystemError("不支持导出为 " + exportFileType + "格式!");
 			}
 		} catch (JRException e) {
-			e.printStackTrace();
-			throw new SystemError(e.getMessage());
-			// return false;
+			throw new SystemError(e);
 		}
-		return true;
 	}
 
 	/**

+ 53 - 10
src/main/webapp/resources/js/preview2/app.js

@@ -4,26 +4,37 @@ PDFJS.workerSrc = 'static/lib/pdf.js/build/pdf.worker.js';
 var canvas = document.getElementById('theCanvas');
 var ctx = canvas.getContext('2d');
 // 隐藏的iframe,用于加载pdf,以便打印(pdf.js自带的打印有问题)
-var hiddenframe = document.getElementById("hiddenFrame");
+var hiddenFrame = document.getElementById("hiddenFrame");
 
 // pdf文件的路径
 var url;
 var pdfDoc;
+//页码
 var pageIndex;
+//总页数
 var pageSize;
+//预览的pdf的缩放级别(为pdf原大小的倍数)
 var scale;
+//浏览器窗口高度
 var winHeight;
+//浏览器窗口宽度
 var winWidth;
+//参数打印类型,可能为PRINT、PREVIEW
+var printType = getParameter("printType");
+//hiddenFrame是否加载成功
+var hiddenFrameLoaded = false;
 
 getWindowWidth();
 loadPdfData();
 
 // 是否立即打印
-var printType = getParameter("printType");
 if (printType && printType == 'PRINT') {
-	hiddenframe.onload = function() {
-		hiddenframe.contentWindow.print();
-	}
+	// hiddenFrame.onload = function() {
+	// }
+//	hiddenFrame.contentWindow.print();
+//	if (hiddenFrameLoaded) {
+//	}
+	printPdf();
 }
 
 // 上页
@@ -84,7 +95,13 @@ $("#print").click(function() {
 	if (!pdfDoc) {
 		return;
 	}
-	hiddenframe.contentWindow.print();
+	printPdf();
+//	console.log("1--loaded.." + hiddenFrameLoaded);
+//	if (hiddenFrameLoaded) {
+//		hiddenFrame.contentWindow.print();
+//	} else {
+//		console.log("2--loaded.." + hiddenFrameLoaded);
+//	}
 });
 
 // 下载pdf
@@ -138,14 +155,36 @@ function getWindowWidth() {
 	}
 }
 
+/**
+ * 打印
+ */
+//TODO delete 为hiddenFrame绑定事件,一旦pdf加载成功,修改hiddenFrameLoaded值
+function printPdf() {
+	console.log(hiddenFrameLoaded);
+	if(hiddenFrameLoaded){
+		return hiddenFrame.contentWindow.print();
+	}
+	//IE的onload事件
+	if (hiddenFrame.attachEvent) {
+		hiddenFrame.attachEvent("onload", function() {
+			hiddenFrameLoaded = true;
+			hiddenFrame.contentWindow.print();
+		})
+	} else {
+		hiddenFrame.onload = function() {
+			hiddenFrameLoaded = true;
+			hiddenFrame.contentWindow.print();
+		};
+	}
+}
+
 /**
  * 发送请求,服务器端进行填充报表、生成pdf文件等操作
  */
 function loadPdfData() {
 	var loadPdfDataUrl = "print/loadPdfData" + window.location.search;
-	if (!pageIndex) {
-		pageIndex = 1;
-		loadPdfDataUrl = loadPdfDataUrl + "&pageIndex=" + pageIndex;
+	if (printType == "PREVIEW") {
+		loadPdfDataUrl = loadPdfDataUrl + "&pageIndex=" + 1;
 	}
 	$.ajax({
 		type : "get",
@@ -154,7 +193,10 @@ function loadPdfData() {
 		success : function(data) {
 			// 返回的pdf文件路径
 			var pdfPath = data.pdfPath;
-			hiddenframe.src = pdfPath;
+			hiddenFrame.src = pdfPath;
+			if (!pageIndex) {
+				pageIndex = 1;
+			}
 			// 获取所对应的分页的pdf文件路径
 			url = pdfPath.replace(".pdf", "_" + pageIndex + ".pdf");
 			pageSize = data.pageSize;
@@ -236,6 +278,7 @@ function getScale(page, multipleOfWindowWidth) {
  * 预览前一页
  */
 function prevPage() {
+	// 验证pdfDoc不存在,是为了避免报表出现编译失败等问题时,仍然试图翻页
 	if (!pdfDoc || pageIndex <= 1) {
 		return;
 	}