Selaa lähdekoodia

file功能迁移

master
郑根木 2 vuotta sitten
vanhempi
commit
efba453a39
18 muutettua tiedostoa jossa 1297 lisäystä ja 0 poistoa
  1. +10
    -0
      smtweb-system/sw-system-bpm/pom.xml
  2. +26
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/config/FileConfig.java
  3. +185
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileDownloadController.java
  4. +157
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileUploadController.java
  5. +63
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/dao/ImageAttachDao.java
  6. +114
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/dao/SysAttachDao.java
  7. +13
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/entity/AttachPathPO.java
  8. +8
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/entity/FileDataVO.java
  9. +15
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/entity/UploadDataVO.java
  10. +106
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FileDynPath.java
  11. +51
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FileFixPath.java
  12. +132
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FilePathGenerator.java
  13. +48
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FilePathInfo.java
  14. +79
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/MemMultipartFile.java
  15. +109
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/ThumbImage.java
  16. +109
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/area/Area.java
  17. +42
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/area/AreaCache.java
  18. +30
    -0
      smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/area/AreaService.java

+ 10
- 0
smtweb-system/sw-system-bpm/pom.xml Näytä tiedosto

@@ -35,6 +35,16 @@
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>[0.4, 0.5)</version>
</dependency>
<dependency>
<groupId>org.jclarion</groupId>
<artifactId>image4j</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.11.0</version>


+ 26
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/config/FileConfig.java Näytä tiedosto

@@ -0,0 +1,26 @@
package cc.smtweb.system.bpm.spring.config;

import cc.smtweb.system.bpm.util.FilePathGenerator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import cc.smtweb.framework.core.db.jdbc.IdGenerator;

/**
* 微服务框架封装自动配置类
*/
@Configuration
public class FileConfig {
// 文件本地存储配置
@Value("${smtweb.file.local-path}")
private String fileLocalPath;

// 文件请求URL路径配置 http://127.0.0.1:${server.port}/${server.servlet.context-path}/files/
@Value("${smtweb.file.url}")
private String fileUrl;

@Bean
public FilePathGenerator filePathGenerator(IdGenerator idGenerator) {
return new FilePathGenerator(fileLocalPath, fileUrl, idGenerator);
}
}

+ 185
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileDownloadController.java Näytä tiedosto

@@ -0,0 +1,185 @@
package cc.smtweb.system.bpm.spring.controller;

import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.session.SessionUtil;
import cc.smtweb.system.bpm.util.FilePathGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.concurrent.TimeUnit;

@RestController
public class FileDownloadController {
private static final MediaType APPLICATION_JAVASCRIPT = new MediaType("application", "javascript");
@Value("${smtweb.static.local-path:}")
private String staticLocalPath;

@Autowired
private FilePathGenerator filePathGenerator;

@Autowired
private RedisManager redisManager;

/** path方式下载文件 */
@GetMapping("/fs/files/**")
public ResponseEntity<InputStreamResource> files(@RequestParam(value="name", required=false) String name,
@RequestParam(value="noCache", required=false) Boolean noCache,
HttpServletRequest request
) throws FileNotFoundException {
String filePath = request.getRequestURI().substring(10);
return download(filePath, name, noCache, request);
}

/** 参数方式下载文件 */
@GetMapping("/fs/download")
public ResponseEntity<InputStreamResource> download(@RequestParam(value="path") String path,
@RequestParam(value="name", required=false) String name,
@RequestParam(value="noCache", required=false) Boolean noCache,
HttpServletRequest request
) throws FileNotFoundException {
SessionUtil.checkSession(request, redisManager);

File file = new File(filePathGenerator.getFileDiskPath(path));

if (!file.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}

if (StringUtils.isBlank(name)) {
name = file.getName();
}

HttpHeaders headers = new HttpHeaders();
if (Boolean.TRUE.equals(noCache)) {
headers.setCacheControl("no-cache, no-store, must-revalidate");
headers.setPragma("no-cache");
headers.setExpires(0);
}

headers.setLastModified(file.lastModified());
headers.add("Content-Disposition",
String.format("attachment; filename=\"%s\"", new String(name.getBytes(StandardCharsets.UTF_8),StandardCharsets.ISO_8859_1)));

return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(new FileInputStream(file)));
}

/** path方式读取静态目录文件 */
@GetMapping("/fs/static/**")
public ResponseEntity<InputStreamResource> resource(@RequestParam(value="default", required=false) String defaultPath,
@RequestParam(value="noCache", required=false) Boolean noCache,
@RequestHeader(value="If-Modified-Since", required = false) String ifModifiedSince,
HttpServletRequest request) throws FileNotFoundException {
String filePath = request.getRequestURI().substring(11);

HttpHeaders headers = new HttpHeaders();

if (Boolean.TRUE.equals(noCache)) {
headers.setCacheControl("no-cache, no-store, must-revalidate");
headers.setPragma("no-cache");
headers.setExpires(0);
} else {
// 暂时缓存1天
headers.setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS));
headers.setExpires(Instant.ofEpochMilli(System.currentTimeMillis() + DateUtils.MILLIS_PER_DAY));
}

String name = getFileName(filePath);
headers.add("Content-Disposition",
String.format("attachment; filename=\"%s\"", new String(name.getBytes(StandardCharsets.UTF_8),StandardCharsets.ISO_8859_1)));

MediaType contentType = getContentType(filePath);
// 先找文件
if (StringUtils.isNotBlank(staticLocalPath)) {
File file = new File(staticLocalPath + filePath);

if (file.exists()) {
headers.setLastModified(file.lastModified());

return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.contentType(contentType)
.body(new InputStreamResource(new FileInputStream(file)));
}
}

// 再找资源目录
InputStream inputStream = getClass().getResourceAsStream("/static/" + filePath);
if (inputStream != null) {
return buildResource(inputStream, contentType, headers);
} else if (StringUtils.isNotBlank(defaultPath)) {
inputStream = getClass().getResourceAsStream("/static/" + defaultPath);
if (inputStream != null) {
return buildResource(inputStream, contentType, headers);
}
}

return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}

private String getFileName(String filePath) {
int pos = filePath.lastIndexOf("/");
if (pos >= 0) {
return filePath.substring(pos + 1);
}

return filePath;
}

private ResponseEntity<InputStreamResource> buildResource(InputStream inputStream, MediaType contentType, HttpHeaders headers) {
return ResponseEntity.ok()
.headers(headers)
// .contentLength(file.length())
.contentType(contentType)
.body(new InputStreamResource(inputStream));
}

private static MediaType getContentType(String filePath) {
int pos = filePath.lastIndexOf(".");

if (pos >= 0) {
String fileExt = filePath.substring(pos + 1).toLowerCase();
switch (fileExt) {
case "htm":
case "html":
case "css":
return MediaType.TEXT_HTML;
case "js":
return APPLICATION_JAVASCRIPT;
case "txt":
return MediaType.TEXT_PLAIN;
case "pdf":
return MediaType.APPLICATION_PDF;
case "xml":
return MediaType.TEXT_XML;
case "gif":
return MediaType.IMAGE_GIF;
case "jpeg":
case "jpg":
return MediaType.IMAGE_JPEG;
case "png":
return MediaType.IMAGE_PNG;
default:
return MediaType.APPLICATION_OCTET_STREAM;
}
}

return MediaType.APPLICATION_OCTET_STREAM;
}
}

+ 157
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/controller/FileUploadController.java Näytä tiedosto

@@ -0,0 +1,157 @@
package cc.smtweb.system.bpm.spring.controller;

import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.session.SessionUtil;
import cc.smtweb.system.bpm.spring.dao.ImageAttachDao;
import cc.smtweb.system.bpm.util.FilePathGenerator;
import cc.smtweb.system.bpm.util.FilePathInfo;
import cc.smtweb.system.bpm.util.MemMultipartFile;
import cc.smtweb.system.bpm.util.ThumbImage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import cc.smtweb.system.bpm.spring.entity.FileDataVO;
import cc.smtweb.system.bpm.spring.entity.UploadDataVO;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;

@RestController
public class FileUploadController {
@Autowired
private FilePathGenerator filePathGenerator;

@Autowired
private DbEngine dbEngine;

@Autowired
private RedisManager redisManager;

@Autowired
private ImageAttachDao imageAttachDao;

// TODO: 权限处理,临时文件处理
@PostMapping("/fs/upload/{path}")
public R upload(@RequestParam("file") MultipartFile file, @PathVariable("path") String path,
@RequestParam(value="thumb", required=false) String thumb,
@RequestParam(value="thumbHeight", required=false) Integer thumbHeight,
@RequestParam(value="commit", required=false) Boolean insert,
@RequestParam(value="keepName", required=false) Boolean keepName,
HttpServletRequest request
) {
SessionUtil.checkSession(request, redisManager);
return uploadFile(path, file, ThumbImage.type(thumb), thumbHeight, insert, keepName);
}

@PostMapping("/fs/uploadImage/{path}")
public R upload(@RequestBody FileDataVO data, @PathVariable("path") String path,
@RequestParam(value="thumb", required=false) String thumb,
@RequestParam(value="thumbHeight", required=false) Integer thumbHeight,
@RequestParam(value="commit", required=false) Boolean insert,
HttpServletRequest request) {
SessionUtil.checkSession(request, redisManager);

MultipartFile file = MemMultipartFile.build(data.getData());
if (file == null) {
return R.error("数据内容格式有错");
}

return uploadFile(path, file, ThumbImage.type(thumb), thumbHeight, insert, false);
}

@PostMapping("/fs/uploadAvatar/{path}")
public R uploadAvatar(@RequestParam("file") MultipartFile file, @PathVariable("path") String path,
@RequestParam(value="size", required=false) Integer size,
@RequestParam(value="commit", required=false) Boolean insert,
@RequestParam(value="keepName", required=false) Boolean keepName,
HttpServletRequest request) {
SessionUtil.checkSession(request, redisManager);
return uploadFile(path, file, ThumbImage.TYPE_AVATAR, size, insert, keepName);
}

// 保存文件和插入数据库数据
@PostMapping("/fs/commit/{path}")
public R commit(@RequestParam("file") MultipartFile file, @PathVariable("path") String path,
@RequestParam(value="thumb", required=false) String thumb,
@RequestParam(value="thumbHeight", required=false) Integer thumbHeight,
@RequestParam(value="keepName", required=false) Boolean keepName,
HttpServletRequest request) {
SessionUtil.checkSession(request, redisManager);
return uploadFile(path, file, ThumbImage.type(thumb), thumbHeight, true, keepName);
}

private R uploadFile(String path, MultipartFile file, int type, Integer size, Boolean insert, Boolean keepName) {
//获取上传时的文件名
String fileName = file.getOriginalFilename();

//判断文件是否为空
if(file.isEmpty() && fileName != null){
return R.error("文件为空");
}

// 判断保持文件名不变
FilePathInfo fileInfo = filePathGenerator.make(path, fileName, Boolean.TRUE.equals(keepName));

// 注意是路径+文件名
File targetFile = new File(fileInfo.getFullFileName());

try(InputStream inputStream = file.getInputStream(); OutputStream outputStream = new FileOutputStream(targetFile)) {
// 最后使用资源访问器FileCopyUtils的copy方法拷贝文件
FileCopyUtils.copy(inputStream, outputStream);
} catch (IOException e) {
//出现异常,则告诉页面失败
return R.error("上传失败", e);
}

// 生成缩略图
// String contentType = file.getContentType();
UploadDataVO data = new UploadDataVO();

data.setPath(fileInfo.getMysqlFilePath());
data.setName(fileName);
data.setSize(file.getSize());
data.setContentType(file.getContentType());
data.setUrl(filePathGenerator.getFileUrl(fileInfo.getMysqlFilePath()));

if (type == ThumbImage.TYPE_THUMB || type == ThumbImage.TYPE_AVATAR) {
try {
imageAttachDao.makeThumb(data, type == ThumbImage.TYPE_THUMB, targetFile, size);
} catch (IOException e) {
return R.error("生成缩略图失败", e);
}
}

if (Boolean.TRUE.equals(insert)) {
Long id = dbEngine.nextId();
Timestamp now = new Timestamp(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");

dbEngine.update("insert into sw_user.sys_attach(attach_id, attach_name, attach_path, attach_content_type, attach_size, attach_create_time) values(?, ?, ?, ?, ?, ?)",
id, data.getName(), data.getPath(), data.getContentType(), data.getSize(), sdf.format(now));

data.setId(id);
}

return R.success(data);
}

// TODO: 修改为安全的后台删除方式
@PostMapping("/fs/remove")
public R remove(@RequestParam(value="filePath") String filePath, HttpServletRequest request) {
SessionUtil.checkSession(request, redisManager);

File file = new File(filePathGenerator.getFileDiskPath(filePath));
if (file.exists() && file.isFile()) {
if (file.delete()) {
R.success(filePath);
}
}

return R.success();
}
}

+ 63
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/dao/ImageAttachDao.java Näytä tiedosto

@@ -0,0 +1,63 @@
package cc.smtweb.system.bpm.spring.dao;

import cc.smtweb.system.bpm.util.ThumbImage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import cc.smtweb.system.bpm.spring.entity.UploadDataVO;

import java.io.File;
import java.io.IOException;

@Service
public class ImageAttachDao {
public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";

public void makeThumb(UploadDataVO data, boolean isThumb, File targetFile, Integer size) throws IOException {
boolean imageType = false;
String fileName = data.getName();
String contentType = data.getContentType();

if (contentType.startsWith("image/")) {
imageType = true;
} else if (contentType.equals(APPLICATION_OCTET_STREAM)) {
String fileExt = fileName.substring(fileName.lastIndexOf("."));

if (StringUtils.isNotEmpty(fileExt)) {
switch (fileExt.toLowerCase()) {
case ".jpg":
case ".jpeg":
contentType = "image/jpg";
imageType = true;
break;
case ".gif":
contentType = "image/gif";
imageType = true;
break;
case ".png":
contentType = "image/png";
imageType = true;
break;
default:
break;
}

if (imageType) {
data.setContentType(contentType);
}
}
}

if (imageType) {
int thumbHeight = 80;
if (size != null) {
thumbHeight = (size > 500) ? 500 : size;
}

ThumbImage thumbImage = new ThumbImage();

thumbImage.makeThumb(isThumb, targetFile, thumbHeight);
data.setWidth(thumbImage.getImageWidth());
data.setHeight(thumbImage.getImageHeight());
}
}
}

+ 114
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/dao/SysAttachDao.java Näytä tiedosto

@@ -0,0 +1,114 @@
package cc.smtweb.system.bpm.spring.dao;

import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.system.bpm.spring.entity.AttachPathPO;
import cc.smtweb.system.bpm.util.FilePathGenerator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.util.UriEncoder;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SysAttachDao {
@Autowired
private FilePathGenerator filePathGenerator;

@Autowired
private DbEngine dbEngine;

/**
* 获取文件本地文件路径
*
* @param filePath 相对路径
* @return 本地文件全路径
*/
public String getDiskPath(String filePath) {
return filePathGenerator.getFileDiskPath(filePath);
}

/**
* 获取访问文件的URL地址
*
* @param filePath 文件相对路径
* @return 文件URL地址
*/
public String getFileUrl(String filePath) {
return filePathGenerator.getFileUrl(filePath);
}

/**
* 获取访问文件的URL地址
*
* @param filePath 文件相对路径
* @param filePath 文件名
* @return 文件URL地址
*/
public String getFileUrl(String filePath, String fileName) {
return "/fs/download?path=" + UriEncoder.encode(filePath) + "&name=" + UriEncoder.encode(fileName);
}

public AttachPathPO get(Long id) {
if (id != null) {
return dbEngine.queryEntity("select attach_id, attach_name, attach_path, attach_content_type, attach_size, attach_create_time from sw_user.sys_attach where attach_id=?",
AttachPathPO.class, id);
}

return null;
}

// 删除文件记录和文件
public void remove(Long fileId) {
// if (id != null) {
// return dbEngine.queryEntity("select attach_id, attach_name, attach_path, attach_content_type, attach_size, attach_create_time from sw_user.sys_attach where attach_id=?",
// AttachPathPO.class, id);
// }
//
// return null;
}

// 删除文件
public void remove(String filePath) {
// if (id != null) {
// return dbEngine.queryEntity("select attach_id, attach_name, attach_path, attach_content_type, attach_size, attach_create_time from sw_user.sys_attach where attach_id=?",
// AttachPathPO.class, id);
// }
//
// return null;
}

public List<AttachPathPO> list(Long[] ids) {
if (ids != null && ids.length > 0) {
return dbEngine.query("select attach_id, attach_name, attach_path, attach_content_type, attach_size, attach_create_time from sw_user.sys_attach where attach_id in( "
+ StringUtils.join(ids, ",") + ")",
AttachPathPO.class);
}

return null;
}

public Map<Long, AttachPathPO> map(Long[] ids) {
List<AttachPathPO> list = list(ids);
if (list != null && !list.isEmpty()) {
Map<Long, AttachPathPO> map = new HashMap<>(list.size());
list.forEach((item) -> map.put(item.getAttachId(), item));

return map;
}

return null;
}

// 保持文件,删除临时文件记录,避免被定时删除
public void retain(String filePath) {

}

// 保持文件,删除临时文件记录,避免被定时删除
public void retain(Long fileId) {

}
}

+ 13
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/entity/AttachPathPO.java Näytä tiedosto

@@ -0,0 +1,13 @@
package cc.smtweb.system.bpm.spring.entity;

import lombok.Data;

@Data
public class AttachPathPO {
private Long attachId;
private String attachName;
private String attachPath;
private String attachContentType;
private Long attachSize;
private Long attachCreate;
}

+ 8
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/entity/FileDataVO.java Näytä tiedosto

@@ -0,0 +1,8 @@
package cc.smtweb.system.bpm.spring.entity;

import lombok.Data;

@Data
public class FileDataVO {
private String data;
}

+ 15
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/spring/entity/UploadDataVO.java Näytä tiedosto

@@ -0,0 +1,15 @@
package cc.smtweb.system.bpm.spring.entity;

import lombok.Data;

@Data
public class UploadDataVO {
private Long id;
private Integer height;
private Integer width;
private long size;
private String path;
private String name;
private String contentType;
private String url;
}

+ 106
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FileDynPath.java Näytä tiedosto

@@ -0,0 +1,106 @@
package cc.smtweb.system.bpm.util;

import cc.smtweb.framework.core.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;

import java.io.File;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Random;

/**
* 动态文件〈文件路径〉
*
* @author kevin
* @since 1.0.0
*/
@Slf4j
public class FileDynPath extends FileFixPath {
// 目录允许的最大文件数量,避免批量导入文件时文件太多
private static final int MAX_FILE_COUNT = 2000;
private static final int MAX_DIR_COUNT = 100000;
private long startTime;
private long endTime;
private final SimpleDateFormat sdf;
// 文件数量
private int fileCount;
// 目录子索引
private int pathIndex;

public FileDynPath(String rootPath, String typeDir, SimpleDateFormat sdf) {
super(rootPath, typeDir);
this.sdf = sdf;
}

/**
* 返回日期路径字符串
*/
@Override
public FilePathInfo makeDatePath(long fileId, String fileExt) {
long now = System.currentTimeMillis();
String fileName;

// 如果不在就需要重新创建子目录
if (now < startTime || now >= endTime) {
startTime = DateUtil.getTimesmorning(now);
endTime = startTime + DateUtils.MILLIS_PER_DAY;

this.path = this.typeDir + "/" + sdf.format(new Timestamp(now));
createFolder(rootPath + this.path);
}

// 如果文件数量太大就需要创建新子目录
while (this.fileCount >= MAX_FILE_COUNT) {
this.pathIndex++;
if(this.pathIndex > MAX_DIR_COUNT) {
throw new RuntimeException("dir is two many");
}

createFolder(rootPath + getSubPath());
}

Random random = new Random();
int randomId = random.nextInt(Integer.MAX_VALUE);

fileName = Long.toHexString(fileId) + "_" + Integer.toHexString(randomId) + fileExt;

return new FilePathInfo(rootPath, getSubPath(), now, fileName, fileId);
}

private String getSubPath() {
if (this.pathIndex > 0) {
return String.format("%s%02d/%04d", this.path, MAX_DIR_COUNT / 1000, this.pathIndex % 1000);
}

return this.path;
}

private boolean createFolder(String path) {
File file = new File(path);
if (file.exists()) {
if (!file.isDirectory()) {
return false;
}

File[] list = file.listFiles();
if (list != null) {
this.fileCount = list.length;
} else {
this.fileCount = 0;
}

return true;
}

if (!file.mkdirs()) {
log.error("unable to create folders {}.", rootPath + this.path);
return false;
}

log.debug("create folders {}.", file);
this.fileCount = 0;

return true;
}
}

+ 51
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FileFixPath.java Näytä tiedosto

@@ -0,0 +1,51 @@
package cc.smtweb.system.bpm.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;

/**
* 〈文件路径〉
*
* @author kevin
* @since 1.0.0
*/
@Slf4j
public class FileFixPath {
protected String path;
protected String rootPath;
protected String typeDir;

public FileFixPath(String rootPath, String typeDir) {
this.rootPath = rootPath;
this.typeDir = typeDir;
}

public FilePathInfo makeDatePath(long fileId, String fileName) {
long now = System.currentTimeMillis();
this.path = this.typeDir + "/";
createFolder(rootPath + this.path);

return new FilePathInfo(rootPath, this.path, now, fileName, fileId);
}

private boolean createFolder(String path) {
File file = new File(path);
if (file.exists()) {
if (!file.isDirectory()) {
return false;
}

return true;
}

if (!file.mkdirs()) {
log.error("unable to create folders {}.", rootPath + this.path);
return false;
}

log.debug("create folders {}.", file);

return true;
}
}

+ 132
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FilePathGenerator.java Näytä tiedosto

@@ -0,0 +1,132 @@
package cc.smtweb.system.bpm.util;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.springframework.web.multipart.MultipartFile;
import cc.smtweb.framework.core.db.jdbc.IdGenerator;

/**
* 文件名生成规则 subDir/[yyyymm]/[d]/[hex(fileid)]_[hex(rand)].[fileExt] 如果文件是图片格式,会生成缩略图,文件名会直接添加.thumb.jpg后缀 规则参数 yyyymm:
* 时间的年月,固定6位字符。如200505 d: 时间的日期,值范围1~31。如5 fileid: 上传文件的ID,hex(int64) rand: 防盗链随机数,hex(int32)。 fileExt: 文件扩展名。
*/
@Slf4j
public class FilePathGenerator {

public static final String THUMB_FILE_EXT = ".thumb.jpg";
// 文件时间是否作为PK
private SimpleDateFormat sdf;
@Getter
private String rootPath;
private Map<String, FileFixPath> fileFxPathMap = new HashMap<>();
private Map<String, FileDynPath> fileDynPathMap = new HashMap<>();
private String fileUrl;
private IdGenerator idGenerator;

public FilePathGenerator(String rootPath, String fileUrl, IdGenerator idGenerator) {
this.fileUrl = fixEnd(fileUrl);
this.idGenerator = idGenerator;
this.rootPath = fixEnd(rootPath);

sdf = new SimpleDateFormat("yyyyMM/dd/");
}

private static String fixEnd(String path) {
if (path.endsWith("/") || path.endsWith("\\")) {
return path;
} else {
return path + "/";
}
}

/**
* 生成文件路径,根据日期分目录存储
*
* @param subPath 子目录,区分不同应用的文件
* @param originalFileName 原始的文件名,用于提取扩展名用
* @return 文件路径信息类
*/
public FilePathInfo make(String subPath, String originalFileName) {
return make(subPath, originalFileName, null, false);
}

public FilePathInfo make(String subPath, String originalFileName, boolean keepName) {
return make(subPath, originalFileName, null, keepName);
}
/**
* 生成文件路径,根据日期分目录存储
*
* @param subPath 子目录,区分不同应用的文件
* @param multipartFile 上传文件流,用于提取扩展名用
* @return 文件路径信息类
*/
public FilePathInfo make(String subPath, MultipartFile multipartFile) {
return make(subPath, multipartFile.getOriginalFilename(), multipartFile.getContentType(), false);
}

private synchronized FilePathInfo make(String subPath, String originFileName, String contentType, boolean keepName) {
if (keepName) {
FileFixPath filePathSub = fileFxPathMap.get(subPath);
if (filePathSub == null) {
filePathSub = new FileFixPath(this.rootPath, subPath);
fileFxPathMap.put(subPath, filePathSub);
}

return filePathSub.makeDatePath(this.idGenerator.nextId(), originFileName);
} else {
FileDynPath filePathSub = fileDynPathMap.get(subPath);
if (filePathSub == null) {
filePathSub = new FileDynPath(this.rootPath, subPath, sdf);
fileDynPathMap.put(subPath, filePathSub);
}

return filePathSub.makeDatePath(this.idGenerator.nextId(), ext(originFileName, contentType));
}
}

private static String ext(String filename, String contentType) {
int index = filename.lastIndexOf(".");

if (index == -1) {
if (contentType != null) {
MimeTypes allTypes = MimeTypes.getDefaultMimeTypes();
try {
MimeType jpeg = allTypes.forName(contentType);
return jpeg.getExtension();
} catch (MimeTypeException e) {
log.error(contentType, e);
}
}

return "";
}

return filename.substring(index);
}

// 根据数据库存储文件路径获取URL
public String getFileUrl(FilePathInfo filePathInfo) {
return this.fileUrl + filePathInfo.getMysqlFilePath();
}

// 根据数据库存储文件路径获取URL
public String getFileUrl(String mysqlFilePath) {
return this.fileUrl + mysqlFilePath;
}

// 根据数据库存储文件路径获取磁盘存储路径
public String getFileDiskPath(String mysqlFilePath) {
return this.rootPath + mysqlFilePath;
}

// 获取下载路径前缀
public String getDownloadUrl() {
return this.fileUrl;
}
}

+ 48
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/FilePathInfo.java Näytä tiedosto

@@ -0,0 +1,48 @@
package cc.smtweb.system.bpm.util;

import lombok.Getter;


/**
* 数据库需要存储
* fileId, fileTime, subPath + fileName
*/
@Getter
public class FilePathInfo {
// 文件ID
private long fileId;
// 文件创建时间,数据库需要存储
private long fileTime;
// 文件子路径
private String subPath;
// 文件名
private String fileName;
// 本地根路径
private String rootPath;

public FilePathInfo(String rootPath, String subPath, long fileTime, String fileName, long fileId) {
this.rootPath = rootPath;
this.subPath = subPath;
this.fileTime = fileTime;
this.fileName = fileName;
this.fileId = fileId;
}

/**
* 获取本地需要存储的文件全路径
*/
public String getFullFileName() {
return getDiskFilePath();
}

public String getDiskFilePath() {
return this.rootPath + subPath + fileName;
}

/**
* 获取数据库存储需要的文件全路径
*/
public String getMysqlFilePath() {
return subPath + fileName;
}
}

+ 79
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/MemMultipartFile.java Näytä tiedosto

@@ -0,0 +1,79 @@
package cc.smtweb.system.bpm.util;

import org.apache.commons.codec.binary.Base64;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

public class MemMultipartFile implements MultipartFile {
private static final String DATA_IMAGE = "data:image/";
private byte[] data;
private String contentType;
private String filename;

public static MemMultipartFile build(String dataUrl) {
if (dataUrl != null && dataUrl.startsWith(DATA_IMAGE)) {
// data:image/png;base64,
int pos1 = dataUrl.indexOf(';', DATA_IMAGE.length());
int pos2 = dataUrl.indexOf(',', DATA_IMAGE.length());
if (pos1 > 0 && pos2 > pos1) {
byte[] data = Base64.decodeBase64(dataUrl.substring(pos2));

if (data != null) {
String contentType = dataUrl.substring(5, pos1);
return new MemMultipartFile(contentType.replace('/', '.'), contentType, data);
}
}
}

return null;
}

private MemMultipartFile(String filename, String contentType, byte[] data) {
this.data = data;
this.contentType = contentType;
this.filename = filename;
}

@Override
public String getName() {
return "data";
}

@Override
public String getOriginalFilename() {
return filename;
}

@Override
public String getContentType() {
return contentType;
}

@Override
public boolean isEmpty() {
return data.length == 0;
}

@Override
public long getSize() {
return data.length;
}

@Override
public byte[] getBytes() throws IOException {
return data;
}

@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}

@Override
public void transferTo(File file) throws IOException, IllegalStateException {
try(FileOutputStream os = new FileOutputStream(file)) {
os.write(data);
}
}
}

+ 109
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/util/ThumbImage.java Näytä tiedosto

@@ -0,0 +1,109 @@
package cc.smtweb.system.bpm.util;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;

import lombok.Getter;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import net.coobird.thumbnailator.resizers.configurations.Antialiasing;
import net.sf.image4j.codec.ico.ICODecoder;
import org.apache.commons.lang3.StringUtils;

/**
* 缩略图生成工具
* @author xkliu
*/
@Getter
public class ThumbImage {
// 图片处理方式
public static final int TYPE_DEFAULT = 1;
public static final int TYPE_THUMB = 2;
public static final int TYPE_AVATAR = 3;

private int imageWidth;
private int imageHeight;

public static int type(String thumb) {
// 解决历史遗留boolean类型
if (StringUtils.isBlank(thumb) || "false".equalsIgnoreCase(thumb)) {
return TYPE_DEFAULT;
}

if ("true".equalsIgnoreCase(thumb)) {
return TYPE_THUMB;
}

return Integer.parseInt(thumb);
}

public void makeThumb(boolean isThumb, File file, int size) throws IOException {
makeThumb(file, size, size, isThumb);
}
// 后台等比压缩后大小最好控制在20k以内
public void makeThumb(File file, int w, int h, boolean keepAspectRatio) throws IOException {
String fileName = file.getName().toLowerCase();

BufferedImage image;

if (fileName.endsWith(".ico")) {
List<BufferedImage> images = ICODecoder.read(file);
image = images.get(images.size() - 1);
} else {
image = ImageIO.read(file);
}

imageWidth = image.getWidth();
imageHeight = image.getHeight();

if (fileName.endsWith(".png") || fileName.endsWith(".gif")) {
// 把透明的图填充白色背景
BufferedImage newBufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
newBufferedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
image = newBufferedImage;
}

Thumbnails.Builder<BufferedImage> builder = Thumbnails.of(image);

if (keepAspectRatio) {
if (h > 0) {
// 高度为基准调整宽度到达原图缩放比例
int imageR = imageWidth * 1000 / imageHeight;
w = h * imageR / 1000;
} else {
// 宽度为基准调整宽度到达原图缩放比例
int imageR = imageHeight * 1000 / imageWidth;
h = w * imageR / 1000;
}

// int r = w * 1000 / h;
// int imageR = imageWidth * 1000 / imageHeight;
// if (r != imageR) {
// w = imageHeight * r / 1000;
// }
} else {
int r = w * 1000 / h;
int imageR = imageWidth * 1000 / imageHeight;

if (r != imageR) {
int width = imageWidth;
int height = imageHeight;
if (r > imageR) {
width = imageHeight * r / 1000;
} else {
height = imageWidth * 1000 / r;
}

builder.sourceRegion(Positions.CENTER, width, height);
}
}

builder.size(w, h).antialiasing(Antialiasing.ON).outputFormat("jpg").outputQuality(0.9)
.toFile(file.getAbsolutePath() + FilePathGenerator.THUMB_FILE_EXT);
}
}

+ 109
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/area/Area.java Näytä tiedosto

@@ -0,0 +1,109 @@
package cc.smtweb.system.bpm.web.sys.user.area;

import cc.smtweb.framework.core.annotation.SwTable;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.db.impl.DefaultEntity;

/**
* Created by 1 at 2022-06-17 07:58:14
* 实体【[行政区划](SYS_AREA)】的Entity类
*/
@SwTable("SYS_AREA")
public class Area extends DefaultEntity {
public static final String ENTITY_NAME = "SYS_AREA";

public Area() {
super(ENTITY_NAME);
}

/** 主键 */
public long getId() {
return getLong("ar_id");
}

/** 主键 */
public void setId(long ar_id) {
put("ar_id", ar_id);
}
/** 编码 */
public String getCode() {
return getStr("ar_code");
}

/** 编码 */
public void setCode(String ar_code) {
put("ar_code", ar_code);
}
/** 名称 */
public String getName() {
return getStr("ar_name");
}

/** 名称 */
public void setName(String ar_name) {
put("ar_name", ar_name);
}
/** 父ID */
public long getParentId() {
return getLong("ar_parent_id");
}

/** 父ID */
public void setParentId(long ar_parent_id) {
put("ar_parent_id", ar_parent_id);
}
/** 级次码 */
public String getLevelCode() {
return getStr("ar_level_code");
}

/** 级次码 */
public void setLevelCode(String ar_level_code) {
put("ar_level_code", ar_level_code);
}
/** 全称 */
public String getFullName() {
return getStr("ar_full_name");
}

/** 全称 */
public void setFullName(String ar_full_name) {
put("ar_full_name", ar_full_name);
}
/** 级次 */
public int getType() {
return getInt("ar_type");
}

/** 级次 */
public void setType(int ar_type) {
put("ar_type", ar_type);
}
/** 状态 */
public boolean isStatu() {
return getBool("ar_statu");
}

/** 状态 */
public void set(boolean ar_statu) {
setBool("ar_statu", ar_statu);
}
/** 备注 */
public String getRemark() {
return getStr("ar_remark");
}

/** 备注 */
public void setRemark(String ar_remark) {
put("ar_remark", ar_remark);
}
/** 排序码 */
public int getSeq() {
return getInt("ar_seq");
}

/** 排序码 */
public void setSeq(int ar_seq) {
put("ar_seq", ar_seq);
}
}

+ 42
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/area/AreaCache.java Näytä tiedosto

@@ -0,0 +1,42 @@
package cc.smtweb.system.bpm.web.sys.user.area;

import cc.smtweb.framework.core.annotation.SwCache;
import cc.smtweb.framework.core.cache.AbstractEntityCache;
import cc.smtweb.framework.core.cache.CacheManager;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

/**
* Created by 1 at 2022-06-17 07:58:14
* 实体【[行政区划](SYS_AREA)】的缓存类
*/
@SwCache(ident = "SYS_AREA", title = "页面定义")
public class AreaCache extends AbstractEntityCache<Area> {
//缓存key:按父ID
public final static String mk_pr = "pr";
//缓存key:按编码
public final static String mk_code = "code";

public static AreaCache getInstance() {
return CacheManager.getIntance().getCache(AreaCache.class);
}

public AreaCache() {
//缓存key:按父ID
regList(mk_pr, "ar_parent_id");
//缓存key:按编码
regList(mk_code, "ar_code");
}

//缓存key:按父ID
public final Set<Area> getByPr(String key) {
return getListByKey(mk_pr, key);
}
//缓存key:按编码
public final Set<Area> getByCode(String key) {
return getListByKey(mk_code, key);
}
}

+ 30
- 0
smtweb-system/sw-system-bpm/src/main/java/cc/smtweb/system/bpm/web/sys/user/area/AreaService.java Näytä tiedosto

@@ -0,0 +1,30 @@
package cc.smtweb.system.bpm.web.sys.user.area;

import cc.smtweb.framework.core.annotation.SwBody;
import cc.smtweb.framework.core.annotation.SwService;
import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.system.bpm.web.engine.dynPage.DynPageService;
import cc.smtweb.framework.core.mvc.service.AbstractHandler;
import cc.smtweb.framework.core.session.UserSession;

/**
* Created by 1 at 2022-06-17 07:58:14
* 页面【[区划卡片]的服务类
*/
@SwService
public class AreaService extends DynPageService {
//public final static String TYPE_DEMO = "demo";
@Override
protected AbstractHandler createHandler(String type) {
return super.createHandler(type);
}

/* demo
//自定义
public R demo(@SwBody SwMap params, UserSession us) {
return pageHandler(params, us, TYPE_DEMO, handler -> ((DemoHandler)handler).demo());
}
*/

}

Ladataan…
Peruuta
Tallenna