浏览代码

为手机端增加下载 pdf 的接口,支持断点续传

sunyj 8 年之前
父节点
当前提交
7bc87ed106

+ 50 - 0
src/main/java/com/uas/report/controller/PdfController.java

@@ -181,4 +181,54 @@ public class PdfController {
 		return result;
 	}
 
+	/**
+	 * 下载 pdf (断点续传)
+	 * 
+	 * @param u
+	 *            当前账套名称,不可为空
+	 * @param pr
+	 *            用于标识请求源(B2C、B2B)是正式、测试还是开发版本:prod、test、dev,可选(UAS等系统不必传递该参数)
+	 * @param r
+	 *            需要导出的报表的名称,不带任何后缀(如导出采购单,即为"Purchase"),不可为空
+	 * @param w
+	 *            where之后的条件(包括where),可为空
+	 * @param o
+	 *            其他参数,区别于w,报表某些字段的值取决于这些参数, JSON格式,数据为键值对,若模板已指定需要的参数,则不可为空
+	 * @param pf
+	 *            请求来自的平台,不可为空
+	 * @param n
+	 *            下载后的文件名称,可为空
+	 * @param request
+	 * @param response
+	 * @throws JRException
+	 * @throws IOException
+	 * @throws DocumentException
+	 * @throws SQLException
+	 * @throws IllegalStateException
+	 */
+	@RequestMapping(value = "/download")
+	@ResponseBody
+	public void download(@RequestParam(required = true) String u, String pr, @RequestParam(required = true) String r,
+			String w, String o, @RequestParam(required = true) String pf, String n, HttpServletRequest request,
+			HttpServletResponse response)
+			throws JRException, IOException, DocumentException, SQLException, IllegalStateException {
+		u = u == null ? null : u.toUpperCase();
+		ReportUtils.checkParameters(u, r);
+
+		// 相对路径
+		String pdfPath = ReportConstants.GENERATED_FILES_PATH + r + "/"
+				+ fileService.generateFileName(u, pr, w, o, ReportConstants.FILE_TYPE_PDF) + "."
+				+ ReportConstants.FILE_TYPE_PDF;
+		File file = new File(ReportConstants.GENERATED_FILES_DIR + pdfPath);
+		String masterOfJrxml = printService.getMasterOfJrxml(u, r);
+		String jrxmlFilePath = fileService.getJrxmlFilePath(masterOfJrxml, r);
+		if (!fileService.isFileValid(file.getPath(), jrxmlFilePath)) {
+			Map<String, Object> map = getPath(u, pr, r, w, o, pf, request, response);
+			Boolean overload = (Boolean) map.get("overload");
+			if (overload) {
+				throw new IllegalStateException("数据量过大,无法提供服务");
+			}
+		}
+		fileService.rangeDownload(file, n == null ? null : n + ".pdf", request, response);
+	}
 }

+ 17 - 1
src/main/java/com/uas/report/service/FileService.java

@@ -1,9 +1,11 @@
 package com.uas.report.service;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.web.multipart.MultipartFile;
@@ -125,11 +127,25 @@ public interface FileService {
 	 * @param data
 	 *            文件数据
 	 * @param fileName
-	 *            文件名称
+	 *            下载后的文件名称
 	 * @param response
 	 */
 	public void download(byte[] data, String fileName, HttpServletResponse response);
 
+	/**
+	 * 下载文件(支持断点续传)
+	 * 
+	 * @param file
+	 *            要下载的文件
+	 * @param fileName
+	 *            下载后的文件名称
+	 * @param request
+	 * @param response
+	 * @throws IOException
+	 */
+	public void rangeDownload(File file, String fileName, HttpServletRequest request, HttpServletResponse response)
+			throws IOException;
+
 	/**
 	 * 递归删除文件(夹)
 	 * 

+ 80 - 0
src/main/java/com/uas/report/service/impl/FileServiceImpl.java

@@ -16,6 +16,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.io.IOUtils;
@@ -295,6 +297,84 @@ public class FileServiceImpl implements FileService {
 		}
 	}
 
+	@Override
+	public void rangeDownload(File file, String fileName, HttpServletRequest request, HttpServletResponse response)
+			throws IOException {
+		if (file == null) {
+			throw new IllegalArgumentException("参数不能为空:file");
+		}
+		if (!file.exists() || !file.isFile()) {
+			throw new IOException("文件不存在或并非文件");
+		}
+		if (StringUtils.isEmpty(fileName)) {
+			fileName = file.getName();
+		}
+		FileInputStream fis = new FileInputStream(file);
+
+		try {
+			// 告知客户端支持 Accept-Ranges
+			response.reset();
+			response.setHeader("Accept-Ranges", "bytes");
+
+			long total = file.length();
+			long start = 0;
+			long end = total - 1;
+			String rangeHeader = request.getHeader("Range");
+			if (!StringUtils.isEmpty(rangeHeader)) {
+				// 告知客户端支持块下载
+				// HTTP/1.1 206 Partial Content
+				response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
+				rangeHeader = rangeHeader.replace("bytes=", "");
+				// bytes=577215295-
+				if (rangeHeader.endsWith("-")) {
+					// 获取文件块的开始字节位置
+					start = Long.parseLong(rangeHeader.replace("bytes=", "").replace("-", ""));
+				}
+				// bytes=961849437-1153518704
+				else {
+					int index = rangeHeader.indexOf("-");
+					if (index < 0) {
+						throw new IllegalArgumentException("Ranger 格式错误:" + request.getHeader("Range"));
+					}
+					// 获取文件块的开始和结束字节位置
+					start = Long.parseLong(rangeHeader.substring(0, index));
+					end = Long.parseLong(rangeHeader.substring(index + 1));
+				}
+				if (start < 0 || start >= total || end < 0 || end >= total || start > end) {
+					throw new IllegalArgumentException("Ranger 格式错误:" + request.getHeader("Range"));
+				}
+				response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + total);
+				fis.skip(start);
+			}
+
+			long contentLength = end - start + 1;
+			response.setHeader("Content-Length", contentLength + "");
+			// response.setContentType("application/octet-stream");
+			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
+
+			ServletOutputStream outputStream = response.getOutputStream();
+			byte[] data = new byte[1024];
+			int i;
+			int count = 0;
+			try {
+				while (count <= contentLength - 1024 && (i = fis.read(data)) != -1) {
+					outputStream.write(data, 0, i);
+					count += i;
+				}
+				if (count < contentLength) {
+					i = fis.read(data, 0, (int) (contentLength - count));
+					outputStream.write(data, 0, i);
+				}
+				outputStream.flush();
+			} catch (IOException e) {
+				logger.error(e.getMessage());
+				return;
+			}
+		} finally {
+			fis.close();
+		}
+	}
+
 	@Override
 	public String delete(String filePath, Boolean isAbsolutePath) throws IOException {
 		// 路径不能为空,不能只包含'/'(根路径),不能含有'..'(不允许通过该方式删除上一级)