♻️ 重构代码, 新增全局异常处理器, 优化类名

This commit is contained in:
zhaojun1998
2019-10-01 09:00:48 +08:00
parent 8fa3d7934d
commit 8aefee9cdf
18 changed files with 342 additions and 130 deletions

View File

@@ -3,14 +3,16 @@ package im.zhaojun.common.controller;
import cn.hutool.core.util.URLUtil;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.constant.ZfileConstant;
import im.zhaojun.common.enums.FileTypeEnum;
import im.zhaojun.common.enums.StorageTypeEnum;
import im.zhaojun.common.exception.SearchDisableException;
import im.zhaojun.common.model.FileItem;
import im.zhaojun.common.model.ResultBean;
import im.zhaojun.common.model.SiteConfig;
import im.zhaojun.common.model.SystemConfig;
import im.zhaojun.common.model.ViewConfig;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.common.service.SystemService;
import im.zhaojun.common.service.ViewConfigService;
import im.zhaojun.common.util.FileComparator;
import im.zhaojun.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,7 +23,6 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
@RequestMapping("/api")
@@ -33,60 +34,53 @@ public class FileController {
private FileService fileService;
@Resource
private SystemConfigService systemConfigService;
private SystemService systemService;
@Resource
private ViewConfigService viewConfigService;
@GetMapping("/list")
public ResultBean list(String path, String sortBy, boolean descending, @RequestParam(required = false) String password) throws Exception {
public ResultBean list(@RequestParam(defaultValue = "/") String path,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String order,
@RequestParam(required = false) String password) throws Exception {
List<FileItem> fileItems = fileService.fileList(StringUtils.removeDuplicateSeparator("/" + URLUtil.decode(path)));
for (FileItem fileItem : fileItems) {
if (ZfileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())) {
String url = StringUtils.removeDuplicateSeparator("/" + fileItem.getPath() + "/" + fileItem.getName());
if (!fileService.getTextContent(url).equals(password)) {
if (password != null && !"".equals(password)) return ResultBean.error("密码错误.");
if (password != null && !"".equals(password)) {
return ResultBean.error("密码错误.");
}
return ResultBean.error("此文件夹需要密码.", ResultBean.REQUIRED_PASSWORD);
}
}
}
// 排序, 先按照文件类型比较, 文件夹在前, 文件在后, 然后根据 sortBy 字段排序, 默认为升序;
fileItems.sort((o1, o2) -> {
FileTypeEnum o1Type = o1.getType();
FileTypeEnum o2Type = o2.getType();
if (o1Type.equals(o2Type)) {
switch (sortBy) {
case "name": return o1.getName().compareTo(o2.getName());
case "time": return o1.getTime().compareTo(o2.getTime());
case "size": return o1.getSize().compareTo(o2.getSize());
default: return o1.getName().compareTo(o2.getName());
}
}
if (o1Type.equals(FileTypeEnum.FOLDER)) {
return -1;
} else {
return 1;
}
});
if (descending) {
Collections.reverse(fileItems);
}
fileItems.sort(new FileComparator(sortBy, order));
filterFileList(fileItems);
return ResultBean.successData(fileItems);
}
/**
* 获取下载链接
* @param path 路径
* @return 下载链接
*
* @param path 路径
* @param name 文件名称
* @return 下载链接
*/
@GetMapping("/downloadUrl")
public ResultBean getDownloadUrl(String path) throws Exception {
return ResultBean.successData(fileService.getDownloadUrl(URLUtil.decode(path)));
public ResultBean getDownloadUrl(@RequestParam String path,
@RequestParam String name) throws Exception {
path = URLUtil.decode(path);
name = URLUtil.decode(name);
String fullPath = StringUtils.concatURL(path, name);
String downloadUrl = fileService.getDownloadUrl(fullPath);
return ResultBean.successData(downloadUrl);
}
/**
@@ -94,7 +88,7 @@ public class FileController {
* @param path 文件路径
* @return 文件内容
*/
@GetMapping("/getContent")
@GetMapping("/content")
public ResultBean getContent(String path) throws Exception {
return ResultBean.successData(fileService.getTextContent(path));
}
@@ -103,10 +97,10 @@ public class FileController {
* 获取系统配置信息和当前页的标题, 文件头, 文件尾信息
* @param path 路径
*/
@GetMapping("/getConfig")
@GetMapping("/config")
public ResultBean getConfig(String path) throws Exception {
SiteConfig config = fileService.getConfig(URLUtil.decode(StringUtils.removeDuplicateSeparator("/" + path + "/")));
config.setSystemConfig(systemConfigService.getSystemConfig());
SiteConfig config = systemService.getConfig(URLUtil.decode(StringUtils.removeDuplicateSeparator("/" + path + "/")));
config.setViewConfig(viewConfigService.getViewConfig());
return ResultBean.successData(config);
}
@@ -116,11 +110,22 @@ public class FileController {
@PostConstruct
@GetMapping("/updateStorageStrategy")
public ResultBean updateConfig() {
SystemConfig systemConfig = systemConfigService.getSystemConfig();
StorageTypeEnum storageStrategy = systemConfig.getStorageStrategy();
ViewConfig viewConfig = viewConfigService.getViewConfig();
StorageTypeEnum storageStrategy = viewConfig.getStorageStrategy();
fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
log.info("当前启用存储类型: {}", storageStrategy.getDescription());
initSearchCache();
// new Thread(() -> {
// log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
// long startTime = System.currentTimeMillis();
// try {
// fileService.selectAllFileList();
// } catch (Exception e) {
// log.error("缓存所有文件失败", e);
// e.printStackTrace();
// }
// long endTime = System.currentTimeMillis();
// log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ( (endTime - startTime) / 1000 ));
// }).start();
return ResultBean.success();
}
@@ -132,6 +137,9 @@ public class FileController {
@GetMapping("/getImageInfo")
public ResultBean getImageInfo(String url) throws Exception {
if (url != null && url.indexOf("//") == 0) {
url = "http:" + url;
}
return ResultBean.success(fileService.getImageInfo(url));
}
@@ -141,7 +149,25 @@ public class FileController {
}
@GetMapping("/search")
public ResultBean search(@RequestParam("path") String name) throws Exception {
public ResultBean search(@RequestParam(value = "name", defaultValue = "/") String name) throws Exception {
ViewConfig viewConfig = viewConfigService.getViewConfig();
if (!viewConfig.getSearchEnable()) {
throw new SearchDisableException("搜索功能未开启");
}
return ResultBean.success(fileService.search(URLUtil.decode(name)));
}
/**
* 过滤文件列表, 不显示密码, 头部和尾部文件.
*/
private void filterFileList(List<FileItem> fileItemList) {
if (fileItemList == null) {
return;
}
fileItemList.removeIf(fileItem -> ZfileConstant.PASSWORD_FILE_NAME.equals(fileItem.getName())
|| ZfileConstant.README_FILE_NAME.equals(fileItem.getName())
|| ZfileConstant.HEADER_FILE_NAME.equals(fileItem.getName()));
}
}

View File

@@ -1,18 +1,17 @@
package im.zhaojun.common.controller;
import im.zhaojun.common.service.SystemConfigService;
import im.zhaojun.common.service.ViewConfigService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@Controller
public class IndexController {
@Resource
private SystemConfigService systemConfigService;
private ViewConfigService viewConfigService;
@GetMapping("/")
public String index() {
@@ -20,19 +19,22 @@ public class IndexController {
}
@GetMapping("/file/**")
public ModelAndView index(ModelAndView modelAndView, HttpServletRequest request) {
public ModelAndView index(ModelAndView modelAndView) {
modelAndView.setViewName("index");
modelAndView.addObject("systemConfig", systemConfigService.getSystemConfig());
modelAndView.addObject("viewConfig", viewConfigService.getViewConfig());
return modelAndView;
}
@GetMapping("/admin")
public ModelAndView admin(ModelAndView modelAndView) {
modelAndView.setViewName("admin");
modelAndView.addObject("systemConfig", systemConfigService.getSystemConfig());
modelAndView.addObject("viewConfig", viewConfigService.getViewConfig());
return modelAndView;
}
@GetMapping("/install")
public ModelAndView install(ModelAndView modelAndView) {
modelAndView.setViewName("install");
return modelAndView;
}
}

View File

@@ -0,0 +1,30 @@
package im.zhaojun.common.exception;
import im.zhaojun.common.model.ResultBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 全局异常处理器
*/
@ControllerAdvice
@ResponseBody
public class GlobleExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobleExceptionHandler.class);
@ExceptionHandler(SearchDisableException.class)
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public ResultBean searchDisableExceptionHandler(SearchDisableException e) {
if (log.isDebugEnabled()) {
log.debug(e.getMessage(), e);
}
return ResultBean.error(e.getMessage());
}
}

View File

@@ -0,0 +1,23 @@
package im.zhaojun.common.exception;
public class SearchDisableException extends RuntimeException {
public SearchDisableException() {
}
public SearchDisableException(String message) {
super(message);
}
public SearchDisableException(String message, Throwable cause) {
super(message, cause);
}
public SearchDisableException(Throwable cause) {
super(cause);
}
public SearchDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -12,6 +12,7 @@ public class FileItem implements Serializable {
private Long size;
private FileTypeEnum type;
private String path;
private String url;
public String getName() {
return name;
@@ -52,4 +53,12 @@ public class FileItem implements Serializable {
public void setPath(String path) {
this.path = path;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -4,6 +4,8 @@ public class ImageInfo {
private Integer width;
private Integer height;
public ImageInfo() {}
public ImageInfo(Integer width, Integer height) {
this.width = width;
this.height = height;

View File

@@ -10,7 +10,7 @@ public class SiteConfig implements Serializable {
private String footer;
private SystemConfig systemConfig;
private ViewConfig viewConfig;
public String getHeader() {
return header;
@@ -28,11 +28,11 @@ public class SiteConfig implements Serializable {
this.footer = footer;
}
public SystemConfig getSystemConfig() {
return systemConfig;
public ViewConfig getViewConfig() {
return viewConfig;
}
public void setSystemConfig(SystemConfig systemConfig) {
this.systemConfig = systemConfig;
public void setViewConfig(ViewConfig viewConfig) {
this.viewConfig = viewConfig;
}
}

View File

@@ -10,9 +10,9 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
@Entity(name = "SYSTEM_CONFIG")
@Data
public class SystemConfig {
public class ViewConfig {
@JsonIgnore
@Id

View File

@@ -1,12 +0,0 @@
package im.zhaojun.common.repository;
import im.zhaojun.common.model.SystemConfig;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SystemConfigRepository extends JpaRepository<SystemConfig, Integer> {
public SystemConfig findFirstBy();
}

View File

@@ -0,0 +1,12 @@
package im.zhaojun.common.repository;
import im.zhaojun.common.model.ViewConfig;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ViewConfigRepository extends JpaRepository<ViewConfig, Integer> {
public ViewConfig findFirstBy();
}

View File

@@ -38,27 +38,6 @@ public interface FileService {
@Cacheable
String getDownloadUrl(String path) throws Exception;
/**
* 构建指定路径下标题, 页头, 页尾
* @param path 路径
*/
@Cacheable
default SiteConfig getConfig(String path) throws Exception {
SiteConfig siteConfig = new SiteConfig();
FileService fileService = (FileService) AopContext.currentProxy();
List<FileItem> fileItemList = fileService.fileList(path);
path = StringUtils.removeLastSeparator(path);
for (FileItem fileItem : fileItemList) {
if (ZfileConstant.README_FILE_NAME.equalsIgnoreCase(fileItem.getName())) {
siteConfig.setFooter(getTextContent(path + "/" + fileItem.getName()));
} else if (ZfileConstant.HEADER_FILE_NAME.equalsIgnoreCase(fileItem.getName())) {
siteConfig.setHeader(getTextContent(path + "/" + fileItem.getName()));
}
}
return siteConfig;
}
/**
* 获取文件内容.
*/

View File

@@ -1,27 +0,0 @@
package im.zhaojun.common.service;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.enums.StorageTypeEnum;
import im.zhaojun.common.model.SystemConfig;
import im.zhaojun.common.repository.SystemConfigRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class SystemConfigService {
@Resource
private SystemConfigRepository systemConfigRepository;
public SystemConfig getSystemConfig() {
return systemConfigRepository.findFirstBy();
}
public FileService getCurrentFileService() {
SystemConfig systemConfig = getSystemConfig();
StorageTypeEnum storageStrategy = systemConfig.getStorageStrategy();
return StorageTypeFactory.getStorageTypeService(storageStrategy);
}
}

View File

@@ -0,0 +1,39 @@
package im.zhaojun.common.service;
import im.zhaojun.common.constant.ZfileConstant;
import im.zhaojun.common.model.FileItem;
import im.zhaojun.common.model.SiteConfig;
import im.zhaojun.common.util.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class SystemService {
@Resource
private ViewConfigService viewConfigService;
/**
* 构建指定路径下标题, 页头, 页尾
* @param path 路径
*/
public SiteConfig getConfig(String path) throws Exception {
SiteConfig siteConfig = new SiteConfig();
FileService fileService = viewConfigService.getCurrentFileService();
List<FileItem> fileItemList = fileService.fileList(path);
path = StringUtils.removeLastSeparator(path);
for (FileItem fileItem : fileItemList) {
if (ZfileConstant.README_FILE_NAME.equalsIgnoreCase(fileItem.getName())) {
siteConfig.setFooter(fileService.getTextContent(path + "/" + fileItem.getName()));
} else if (ZfileConstant.HEADER_FILE_NAME.equalsIgnoreCase(fileItem.getName())) {
siteConfig.setHeader(fileService.getTextContent(path + "/" + fileItem.getName()));
}
}
return siteConfig;
}
}

View File

@@ -0,0 +1,27 @@
package im.zhaojun.common.service;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.enums.StorageTypeEnum;
import im.zhaojun.common.model.ViewConfig;
import im.zhaojun.common.repository.ViewConfigRepository;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ViewConfigService {
@Resource
private ViewConfigRepository viewConfigRepository;
public ViewConfig getViewConfig() {
return viewConfigRepository.findFirstBy();
}
public FileService getCurrentFileService() {
ViewConfig viewConfig = getViewConfig();
StorageTypeEnum storageStrategy = viewConfig.getStorageStrategy();
return StorageTypeFactory.getStorageTypeService(storageStrategy);
}
}

View File

@@ -0,0 +1,47 @@
package im.zhaojun.common.util;
import im.zhaojun.common.enums.FileTypeEnum;
import im.zhaojun.common.model.FileItem;
import java.util.Comparator;
/**
* 文件比较器
*
* - 文件夹始终比文件排序高
* - 默认按照名称排序
* - 默认排序为升序
* - 按名称排序不区分大小写
*/
public class FileComparator implements Comparator<FileItem> {
private String sortBy;
private String order;
public FileComparator(String sortBy, String order) {
this.sortBy = sortBy;
this.order = order;
}
@Override
public int compare(FileItem o1, FileItem o2) {
FileTypeEnum o1Type = o1.getType();
FileTypeEnum o2Type = o2.getType();
if (o1Type.equals(o2Type)) {
int result;
switch (sortBy) {
case "time": result = o1.getTime().compareTo(o2.getTime()); break;
case "size": result = o1.getSize().compareTo(o2.getSize()); break;
default: result = o1.getName().compareToIgnoreCase(o2.getName());
}
return "asc".equals(order) ? result : -result;
}
if (o1Type.equals(FileTypeEnum.FOLDER)) {
return -1;
} else {
return 1;
}
}
}

View File

@@ -0,0 +1,45 @@
package im.zhaojun.common.util;
import im.zhaojun.common.config.StorageTypeFactory;
import im.zhaojun.common.enums.StorageTypeEnum;
import im.zhaojun.common.model.ViewConfig;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.ViewConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 项目启动监听器, 当项目启动时, 遍历当前对象存储的所有内容, 添加到缓存中.
*/
@Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
private static final Logger log = LoggerFactory.getLogger(StartupListener.class);
@Resource
private ViewConfigService viewConfigService;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ViewConfig viewConfig = viewConfigService.getViewConfig();
StorageTypeEnum storageStrategy = viewConfig.getStorageStrategy();
FileService fileService = StorageTypeFactory.getStorageTypeService(storageStrategy);
new Thread(() -> {
log.info("缓存 {} 所有文件开始", storageStrategy.getDescription());
long startTime = System.currentTimeMillis();
try {
fileService.selectAllFileList();
} catch (Exception e) {
log.error("缓存所有文件失败", e);
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
log.info("缓存 {} 所有文件结束, 用时: {} 秒", storageStrategy.getDescription(), ( (endTime - startTime) / 1000 ));
}).start();
}
}

View File

@@ -24,6 +24,11 @@ public class StringUtils {
return path;
}
public static String concatURL(String path, String name) {
return removeDuplicateSeparator("/" + path + "/" + name);
}
/**
* 将域名和路径组装成 URL, 主要用来处理分隔符 '/'
* @param domain 域名

View File

@@ -10,6 +10,7 @@ import im.zhaojun.common.model.StorageConfig;
import im.zhaojun.common.service.FileService;
import im.zhaojun.common.service.StorageConfigService;
import im.zhaojun.common.util.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@@ -20,6 +21,8 @@ import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
@@ -31,6 +34,12 @@ public class LocalService implements FileService {
private static final String FILE_PATH_KEY = "filePath";
@Value("${server.port}")
private String port;
@Value("${server.servlet.context-path}")
private String contextPath;
@Resource
private StorageConfigService storageConfigService;
@@ -61,8 +70,11 @@ public class LocalService implements FileService {
fileItem.setTime(new Date(f.lastModified()));
fileItem.setSize(f.length());
fileItem.setName(f.getName());
fileItemList.add(fileItem);
fileItem.setPath(path);
if (!f.isDirectory()) {
fileItem.setUrl(getDownloadUrl(StringUtils.concatURL(path, f.getName())));
}
fileItemList.add(fileItem);
}
return fileItemList;
@@ -70,16 +82,9 @@ public class LocalService implements FileService {
@Override
public String getDownloadUrl(String path) throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 网络协议
String networkProtocol = request.getScheme();
// 网络ip
String host = request.getServerName();
// 端口号
int port = request.getServerPort();
// 项目发布名称
String webApp = request.getContextPath();
return StringUtils.concatPath(networkProtocol + "://" + host + ":" + port + webApp, "local-download?fileName=" + path);
InetAddress localHost = Inet4Address.getLocalHost();
String host = localHost.getHostAddress();
return StringUtils.concatPath( "//" + host + ":" + port + contextPath, "local-download?fileName=" + path);
}
@Override