Pārlūkot izejas kodu

支持浏览器查看、下载模板;解决下载文件名带中文时乱码的问题;压缩时对路径下的zip、jasper文件不进行压缩;

sunyj 9 gadi atpakaļ
vecāks
revīzija
bb2ea4041b

+ 13 - 4
src/main/java/com/uas/report/controller/FileController.java

@@ -2,6 +2,7 @@ package com.uas.report.controller;
 
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
@@ -50,11 +51,13 @@ public class FileController {
 	 * 
 	 * @param filePath
 	 *            文件路径
+	 * @param isAbsolutePath
+	 *            文件路径是否为绝对路径
 	 * @param response
 	 */
 	@RequestMapping("/download")
-	public void download(String filePath, HttpServletResponse response) {
-		fileService.download(filePath, response);
+	public void download(String filePath, Boolean isAbsolutePath, HttpServletResponse response) {
+		fileService.download(filePath, isAbsolutePath, response);
 	}
 
 	/**
@@ -73,7 +76,7 @@ public class FileController {
 		if (zipFilePath.isEmpty()) {
 			throw new ReportException("压缩失败");
 		}
-		fileService.download(zipFilePath, response);
+		fileService.download(zipFilePath, true, response);
 	}
 
 	/**
@@ -87,7 +90,7 @@ public class FileController {
 	 */
 	@RequestMapping("/download/jrxml")
 	public void downloadJrxml(String userName, String reportName, HttpServletResponse response) {
-		fileService.download(fileService.getJrxmlFilePath(userName, reportName), response);
+		fileService.download(fileService.getJrxmlFilePath(userName, reportName), true, response);
 	}
 
 	/**
@@ -126,4 +129,10 @@ public class FileController {
 		}
 	}
 
+	@RequestMapping("/listFiles")
+	@ResponseBody
+	public List<Map<String, Object>> listFiles(String fileRelativePath) {
+		return fileService.listFiles(fileRelativePath);
+	}
+
 }

+ 3 - 1
src/main/java/com/uas/report/controller/PrintController.java

@@ -4,6 +4,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -170,7 +171,8 @@ public class PrintController {
 			} else {
 				exportFileName += "." + exportFileType;
 			}
-			response.setHeader("Content-Disposition", "attachment;filename=" + exportFileName);
+			response.setHeader("Content-Disposition",
+					"attachment;filename=" + URLEncoder.encode(exportFileName, "UTF-8"));
 			OutputStream outputStream = response.getOutputStream();
 			outputStream.write(data);
 			outputStream.flush();

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

@@ -1,5 +1,8 @@
 package com.uas.report.service;
 
+import java.util.List;
+import java.util.Map;
+
 import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.web.multipart.MultipartFile;
@@ -73,9 +76,11 @@ public interface FileService {
 	 * 
 	 * @param filePath
 	 *            文件路径
+	 * @param isAbsolutePath
+	 *            文件路径是否为绝对路径
 	 * @param response
 	 */
-	public void download(String filePath, HttpServletResponse response);
+	public void download(String filePath, Boolean isAbsolutePath, HttpServletResponse response);
 
 	/**
 	 * 账套下的某个文件(夹)
@@ -97,6 +102,16 @@ public interface FileService {
 	 */
 	public String delete(String filePath);
 
+	/**
+	 * 列出本地资源根路径下指定路径的文件信息
+	 * 
+	 * @param fileRelativePath
+	 *            指定的相对路径
+	 * @return 文件信息,包括name(String)、lastModified(String)、size(Long)、relativePath(
+	 *         String)、isDirectory(Boolean)
+	 */
+	public List<Map<String, Object>> listFiles(String fileRelativePath);
+
 	/**
 	 * 判断文件是否有效(文件存在并且未过有效期,并且比模板新)
 	 * 

+ 141 - 5
src/main/java/com/uas/report/service/impl/FileServiceImpl.java

@@ -1,13 +1,20 @@
 package com.uas.report.service.impl;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
 
@@ -43,6 +50,7 @@ public class FileServiceImpl implements FileService {
 
 	@Override
 	public String autoDeploy(String userNames) {
+		logger.info("request... " + userNames);
 		if (StringUtils.isEmpty(userNames)) {
 			throw new ReportException("参数不能为空:userNames");
 		}
@@ -173,27 +181,48 @@ public class FileServiceImpl implements FileService {
 		String folderPath = getMasterPath(userName);
 		// 压缩后的压缩包路径,与账套在同一级
 		String zipFilePath = folderPath + ".zip";
-		ZipUtils.zipFolder(folderPath, zipFilePath);
+		FileFilter fileFilter = new FileFilter() {
+			@Override
+			public boolean accept(File pathname) {
+				// 对zip、jasper文件不进行压缩
+				if (pathname.getPath().endsWith(".zip") || pathname.getPath().endsWith(".jasper")) {
+					return false;
+				}
+				return true;
+			}
+		};
+		ZipUtils.zipFolder(folderPath, zipFilePath, fileFilter);
 		return zipFilePath;
 	}
 
 	@Override
-	public void download(String filePath, HttpServletResponse response) {
+	public void download(String filePath, Boolean isAbsolutePath, HttpServletResponse response) {
 		if (StringUtils.isEmpty(filePath) || response == null) {
 			throw new ReportException("参数不能为空:filePath,response");
 		}
+		logger.info("request... " + filePath);
+		if (isAbsolutePath == null || !isAbsolutePath) {
+			filePath = sysConf.getLocalBaseDir() + "/" + filePath;
+		}
 		File file = new File(filePath);
 		if (!file.exists()) {
 			throw new ReportException("文件不存在:" + filePath);
 		}
-		if (!file.isFile()) {
-			throw new ReportException("并非文件:" + filePath);
+		// 下载文件夹之前,需进行压缩
+		if (file.isDirectory()) {
+			String zipFilePath = getZip(getRelativePath(file));
+			if (zipFilePath.isEmpty()) {
+				throw new ReportException("压缩失败");
+			}
+			download(zipFilePath, true, response);
+			return;
 		}
 		try {
 			InputStream inputStream = new FileInputStream(file);
 			byte[] data = new byte[inputStream.available()];
 			inputStream.read(data);
-			response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
+			response.setHeader("Content-Disposition",
+					"attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
 			OutputStream outputStream = response.getOutputStream();
 			outputStream.write(data);
 			outputStream.flush();
@@ -214,6 +243,7 @@ public class FileServiceImpl implements FileService {
 
 	@Override
 	public String delete(String filePath) {
+		logger.info("request... " + filePath);
 		if (StringUtils.isEmpty(filePath)) {
 			throw new ReportException("参数不能为空:filePath");
 		}
@@ -225,6 +255,111 @@ public class FileServiceImpl implements FileService {
 		return filePath;
 	}
 
+	@Override
+	public List<Map<String, Object>> listFiles(String fileRelativePath) {
+		logger.info("request... " + fileRelativePath);
+		// 初始目录为本地资源根路径
+		String filePath = sysConf.getLocalBaseDir();
+		if (!StringUtils.isEmpty(fileRelativePath)) {
+			if (!fileRelativePath.startsWith("/")) {
+				filePath += "/";
+			}
+			filePath += fileRelativePath;
+		}
+		final File file = new File(filePath);
+		if (!file.exists()) {
+			throw new ReportException("文件不存在:" + filePath);
+		}
+
+		List<Map<String, Object>> result = new ArrayList<>();
+		// 如果是文件,直接获取文件信息
+		if (file.isFile()) {
+			result.add(getFileInformation(file));
+		} else {
+			FileFilter fileFilter = new FileFilter() {
+				@Override
+				public boolean accept(File pathname) {
+					// 不显示zip压缩包、jasper文件的信息
+					if (pathname.getPath().endsWith(".zip") || pathname.getPath().endsWith(".jasper")) {
+						return false;
+					}
+					return true;
+				}
+			};
+			File[] files = file.listFiles(fileFilter);
+			// 文件夹放在前面展示
+			List<File> directoryList = new ArrayList<>();
+			List<File> fileList = new ArrayList<>();
+			for (File f : files) {
+				if (f.isDirectory()) {
+					directoryList.add(f);
+				} else {
+					fileList.add(f);
+				}
+			}
+			result.addAll(getFileInformations(directoryList));
+			result.addAll(getFileInformations(fileList));
+		}
+		return result;
+	}
+
+	/**
+	 * 获取多个文件的信息
+	 * 
+	 * @param files
+	 *            文件
+	 * @return 文件信息,包括name(String)、lastModified(String)、size(Long)、relativePath(
+	 *         String)、isDirectory(Boolean)
+	 */
+	private List<Map<String, Object>> getFileInformations(List<File> files) {
+		List<Map<String, Object>> informationList = new ArrayList<>();
+		for (File file : files) {
+			informationList.add(getFileInformation(file));
+		}
+		return informationList;
+	}
+
+	/**
+	 * 获取文件信息
+	 * 
+	 * @param file
+	 *            文件
+	 * @return 文件信息,包括name(String)、lastModified(String)、size(Long)、relativePath(
+	 *         String)、isDirectory(Boolean)
+	 */
+	private Map<String, Object> getFileInformation(File file) {
+		if (file == null || !file.exists()) {
+			return null;
+		}
+		Map<String, Object> information = new HashMap<>();
+		information.put("name", file.getName());
+		information.put("relativePath", getRelativePath(file));
+		information.put("lastModified",
+				new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(file.lastModified())));
+		if (file.isFile()) {
+			information.put("size", file.length());
+			information.put("isDirectory", false);
+		} else {
+			information.put("isDirectory", true);
+		}
+		return information;
+	}
+
+	/**
+	 * 获取文件相对于本地资源根路径的路径
+	 * 
+	 * @param file
+	 *            文件
+	 * @return 相对于本地资源根路径的路径
+	 */
+	private String getRelativePath(File file) {
+		if (file == null || !file.exists()) {
+			return null;
+		}
+		// 获取相对路径,须将本地资源根路径替换掉,并且文件分隔符统一使用 '/'
+		return file.getPath().replace(new File(sysConf.getLocalBaseDir()).getPath(), "").replace("\\", "/");
+	}
+
 	@Override
 	public boolean isFileValid(String filePath, String jrxmlFilePath) {
 		if (!StringUtils.isEmpty(filePath) && !StringUtils.isEmpty(jrxmlFilePath)) {
@@ -297,4 +432,5 @@ public class FileServiceImpl implements FileService {
 			throw new ReportException(e).setDetailedMessage(e);
 		}
 	}
+
 }

+ 14 - 4
src/main/java/com/uas/report/util/ZipUtils.java

@@ -1,6 +1,7 @@
 package com.uas.report.util;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -33,8 +34,10 @@ public class ZipUtils {
 	 *            将压缩的文件夹
 	 * @param zipFilePath
 	 *            压缩后的压缩包路径
+	 * @param fileFilter
+	 *            文件过滤器,不压缩某些文件
 	 */
-	public static void zipFolder(String sourceFolderPath, String zipFilePath) {
+	public static void zipFolder(String sourceFolderPath, String zipFilePath, FileFilter fileFilter) {
 		if (StringUtils.isEmpty(sourceFolderPath) || StringUtils.isEmpty(zipFilePath)) {
 			return;
 		}
@@ -49,7 +52,7 @@ public class ZipUtils {
 
 		try {
 			ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFilePath));
-			putNextEntryFromFolder(zipOutputStream, folder, "");
+			putNextEntryFromFolder(zipOutputStream, folder, "", fileFilter);
 			zipOutputStream.close();
 			logger.info("Zip finished to... " + zipFilePath);
 		} catch (IOException e) {
@@ -66,14 +69,21 @@ public class ZipUtils {
 	 *            需要压缩的文件夹
 	 * @param prefix
 	 *            为保持压缩后的路径层次不变,所记录的当前文件夹的相对层级
+	 * @param fileFilter
+	 *            文件过滤器,不压缩某些文件
 	 */
-	private static void putNextEntryFromFolder(ZipOutputStream zipOutputStream, File folder, String prefix) {
+	private static void putNextEntryFromFolder(ZipOutputStream zipOutputStream, File folder, String prefix,
+			FileFilter fileFilter) {
 		File[] files = folder.listFiles();
 		try {
 			for (File file : files) {
 				if (file.isDirectory()) {
-					putNextEntryFromFolder(zipOutputStream, file, prefix + file.getName() + File.separator);
+					putNextEntryFromFolder(zipOutputStream, file, prefix + file.getName() + File.separator, fileFilter);
 				} else {
+					// 过滤某些文件
+					if (fileFilter != null && !fileFilter.accept(file)) {
+						continue;
+					}
 					zipOutputStream.putNextEntry(new ZipEntry(prefix + file.getName()));
 					InputStream inputStream = new FileInputStream(file);
 					int b;

+ 9 - 2
src/main/webapp/WEB-INF/views/console.html

@@ -36,8 +36,14 @@
 				</ol>
 			</ol>
 
-			<h2>2. 上传、下载、删除</h2>
+			<h2>2. 查看、上传、下载、删除</h2>
 			<ol>
+				<strong><li class="title1">查看</li></strong>
+				<ol>
+					<li><a target="_blank">files</a></li>
+					<li><a target="_blank">files?relativePath=UAS/jrxml</a></li>
+				</ol>
+
 				<strong><li class="title1">上传</li></strong>
 				<ol>
 					<li><a target="_blank">fileUpload?userName=UAS</a></li>
@@ -45,7 +51,8 @@
 				</ol>
 				<strong><li class="title1">下载</li></strong>
 				<ol>
-					<li><a target="_blank">file/download?filePath=C:/sunyj/过程文档/sql/aq.sql</a></li>
+					<li><a target="_blank">file/download?filePath=/UAS/jrxml/Purchase.jrxml</a></li>
+					<li><a target="_blank">file/download?filePath=C:/sunyj/过程文档/sql/aq.sql&isAbsolutePath=true</a></li>
 					<li><a target="_blank">file/download/zip?userName=UAS</a></li>
 					<li><a target="_blank">file/download/jrxml?userName=UAS&reportName=Purchase</a></li>
 				</ol>

+ 52 - 0
src/main/webapp/WEB-INF/views/files.html

@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Report Files</title>
+<link rel="stylesheet" href="static/css/files.css">
+<link rel="stylesheet"
+	href="static/lib/fontawesome/css/font-awesome.min.css">
+</head>
+<body>
+	<div id="listContainer">
+		<div id="topContainer">
+			<div id="currentPathContainer">
+				<span>当前路径:<span id="currentPath"></span></span>
+			</div>
+			<div id="searchContainer">
+				<span>搜索: <input class="search" /></span>
+			</div>
+			<br/><br/>
+			<div id="listHeaderContainer">
+				<div class="listHeader columnNameHeader">
+					<span>名称</span>
+				</div>
+				<div class="listHeader columnSize">
+					<span>大小</span>
+				</div>
+				<div class="listHeader columnLastModified">
+					<span>修改日期</span>
+				</div>
+				<div class="listHeader columnDownload">
+					<span>下载</span>
+				</div>
+			</div>
+		</div>
+		<div id="listContentContainer">
+			<div id="parentPathContainer">
+				<i class='fa fa-level-up' aria-hidden='true'></i>&nbsp&nbsp&nbsp&nbsp
+				<span id="parentPath" class="likeHref">上级目录</span>
+			</div>
+		</div>
+	</div>
+	<div id="errorMessageContainer" hidden="true">
+		<p id="message"></p>
+		<button id="detailedMessageButton" title="获取更多错误信息" hidden="true">更多信息</button>
+		<p id="detailedMessage" hidden="true"></p>
+	</div>
+</body>
+
+<script src="static/lib/jquery/jquery.min.js"></script>
+<script src="static/js/preview/utils.js"></script>
+<script src="static/js/files/app.js"></script>
+</html>

+ 1 - 0
src/main/webapp/WEB-INF/webmvc.xml

@@ -35,4 +35,5 @@
 	<mvc:view-controller path="/fileUpload" view-name="fileUpload" />
 	<mvc:view-controller path="/preview" view-name="preview" />
 	<mvc:view-controller path="/preview2" view-name="preview2" />
+	<mvc:view-controller path="/files" view-name="files" />
 </beans>

+ 82 - 0
src/main/webapp/resources/css/files.css

@@ -0,0 +1,82 @@
+body {
+	font-family: microsoft yahei ui, courier;
+	margin: 20px 120px 20px 120px;
+}
+
+#currentPathContainer {
+	float: left;
+	margin-left: 3%;
+}
+
+#searchContainer {
+	float: right;
+}
+
+.columnNameHeader {
+	width: 45%;
+	float: left;
+	text-align: center;
+}
+
+.columnName, #parentPathContainer {
+	width: 35%;
+	margin-left: 10%;
+}
+
+.columnName {
+	float: left;
+	text-align: left;
+}
+
+.columnSize, .columnDownload {
+	width: 15%;
+	float: left;
+	text-align: center;
+}
+
+.columnLastModified {
+	width: 25%;
+	float: left;
+	text-align: center;
+}
+
+.listHeader {
+	height: 30px;
+	background-color: #606060;
+	color: #fff;
+	line-height: 30px;
+}
+
+#parentPathContainer {
+	text-align: left;
+	margin-bottom: 8px;
+}
+
+#listContentContainer {
+	margin-top: 40px;
+}
+
+.listItem {
+	height: 30px;
+	margin-top: 0px;
+	margin-bottom: 0px;
+}
+
+.overflowHidden {
+	overflow: hidden;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
+
+.likeHref {
+	color: blue;
+	text-decoration: underline;
+	cursor: pointer;
+}
+
+button {
+	background-color: #fff;
+	border: 0px;
+	outline: none;
+	cursor: pointer;
+}

+ 119 - 0
src/main/webapp/resources/js/files/app.js

@@ -0,0 +1,119 @@
+var currentPath = new Object();
+currentPath.value = getParameter("relativePath") || "/";
+if (currentPath.value.charAt(0) != "/") {
+	currentPath.value = "/" + currentPath.value;
+}
+listFiles(currentPath.value);
+
+$("#parentPath").click(function() {
+	var parent = currentPath.parent;
+	if (parent) {
+		currentPath = parent;
+		listFiles(currentPath.value);
+	}
+});
+
+/**
+ * 请求文件信息
+ * 
+ * @param path
+ *            文件路径
+ */
+function listFiles(path) {
+	$("#currentPath").text(currentPath.value);
+	if(!currentPath.parent){
+		$("#parentPathContainer").attr("hidden","true");
+	}else{
+		$("#parentPathContainer").removeAttr("hidden");
+	}
+	var listFilesUrl = "file/listFiles";
+	if (path) {
+		listFilesUrl += "?fileRelativePath=" + path;
+	}
+	$.ajax({
+		type : "get",
+		url : listFilesUrl,
+		success : function(data) {
+			$(".listItem").remove();
+			for (var i = 0; i < data.length; i++) {
+				addList(data[i].name, data[i].size, data[i].lastModified,
+						data[i].relativePath, data[i].isDirectory);
+			}
+		},
+		error : function(XMLHttpRequest) {
+			$("#errorMessageContainer").removeAttr("hidden");
+			// 处理后台传输的自定义的换行标志
+			var result = JSON.parse(XMLHttpRequest.responseText);
+			var message = result.message;
+			$("#message").html(message);
+			if (result.detailedMessage) {
+				$("#detailedMessageButton").removeAttr("hidden");
+			}
+			$("#detailedMessageButton").click(function() {
+				$("#detailedMessage").html(result.detailedMessage);
+				$("#detailedMessage").removeAttr("hidden");
+			});
+		}
+	});
+}
+
+/**
+ * 文件列表增加一行
+ * 
+ * @param name
+ *            文件名称
+ * @param size
+ *            文件大小
+ * @param lastModified
+ *            文件最近修改时间
+ * @param relativePath
+ *            文件相对路径
+ * @param isDirectory
+ *            是否为文件夹
+ */
+function addList(name, size, lastModified, relativePath, isDirectory) {
+	if (size || size == 0) {
+		size += " B";
+	} else {
+		size = "...";
+	}
+	// 显示文件、文件夹图标
+	var fileIcon;
+	if (isDirectory) {
+		fileIcon = "fa fa-folder-open-o";
+	} else {
+		fileIcon = "fa fa-file-o";
+	}
+
+	var listItemDiv = $("<div class='listItem'></div>");
+	var nameDiv = $("<div class='columnName overflowHidden'><i class='"
+			+ fileIcon
+			+ "' aria-hidden='true'></i>&nbsp&nbsp&nbsp&nbsp<span class='likeHref'>"
+			+ name + "</span></div>");
+	var sizeDiv = $("<div class='columnSize'><span>" + size + "</span></div>");
+	var lastModifiedDiv = $("<div class='columnLastModified'><span>"
+			+ lastModified + "</span></div>");
+	var downloadDiv = $("<div class='columnDownload'><button id='downloadButton'><i class='fa fa-download fa-lg' aria-hidden='true'></i></button></div>");
+	listItemDiv.append(nameDiv);
+	listItemDiv.append(sizeDiv);
+	listItemDiv.append(lastModifiedDiv);
+	listItemDiv.append(downloadDiv);
+	$('#listContentContainer').append(listItemDiv);
+
+	nameDiv.click(function() {
+		if (isDirectory) {
+			// 记录父目录
+			var pathCopy = currentPath;
+			currentPath = new Object();
+			currentPath.value = relativePath;
+			currentPath.parent = pathCopy;
+			listFiles(currentPath.value);
+		} else {
+			window.open("file/download?filePath=" + relativePath);
+		}
+	});
+	
+	downloadDiv.click(function(){
+		window.open("file/download?filePath=" + relativePath);
+	});
+}