Browse Source

文件分片上传,断点续传

dev-opt-homepage
chenzhihang 1 year ago
parent
commit
944c3cc919
16 changed files with 1474 additions and 0 deletions
  1. +200
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/file/FileController.java
  2. +142
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/TChunkInfo.java
  3. +161
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/TFileInfo.java
  4. +69
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/UploadResult.java
  5. +23
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/TChunkInfoDao.java
  6. +27
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/TFileInfoDao.java
  7. +36
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ChunkService.java
  8. +29
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/FileInfoService.java
  9. +34
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ChunkServiceImpl.java
  10. +46
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/FileInfoServiceImpl.java
  11. +97
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/FileInfoUtils.java
  12. +41
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ServletUtils.java
  13. +149
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/SnowflakeIdWorker.java
  14. +92
    -0
      ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/TFileInfoVO.java
  15. +150
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/TChunkInfoMapper.xml
  16. +178
    -0
      ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/TFileInfoMapper.xml

+ 200
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/file/FileController.java View File

@@ -0,0 +1,200 @@
package com.ruoyi.platform.controller.file;

import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.platform.domain.TChunkInfo;
import com.ruoyi.platform.domain.TFileInfo;
import com.ruoyi.platform.domain.UploadResult;
import com.ruoyi.platform.service.ChunkService;
import com.ruoyi.platform.service.FileInfoService;
import com.ruoyi.platform.utils.FileInfoUtils;
import com.ruoyi.platform.utils.ServletUtils;
import com.ruoyi.platform.vo.TFileInfoVO;
import io.swagger.annotations.Api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
* 上传下载文件
* @author JaredJia
*
*/
@RestController
@RequestMapping("/uploader")
@Api("文件上传")
public class FileController extends BaseController {

@Value("${git.localPath}/temp")
String uploadFolder;

@Resource
private FileInfoService fileInfoService;
@Resource
private ChunkService chunkService;

private final Logger logger = LoggerFactory.getLogger(FileController.class);
/**
* 上传文件块
* @param chunk
* @return
*/
@PostMapping("/chunk")
public GenericsAjaxResult<String> uploadChunk(TChunkInfo chunk) throws IOException {
String apiRlt = "200";
MultipartFile file = chunk.getUpfile();
logger.info("file originName: {}, chunkNumber: {}", file.getOriginalFilename(), chunk.getChunkNumber());

try {
byte[] bytes = file.getBytes();
Path path = Paths.get(FileInfoUtils.generatePath(uploadFolder, chunk));
//文件写入指定路径
Files.write(path, bytes);
if(chunkService.saveChunk(chunk) < 0) apiRlt = "415";
} catch (IOException e) {
throw new IOException(e.getMessage());
}
return genericsSuccess(apiRlt);
}

@GetMapping("/chunk")
public GenericsAjaxResult<UploadResult> checkChunk(TChunkInfo chunk, HttpServletResponse response) {
UploadResult ur = new UploadResult();
//默认返回其他状态码,前端不进去checkChunkUploadedByResponse函数,正常走标准上传
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
String file = uploadFolder + "/" + chunk.getIdentifier() + "/" + chunk.getFilename();
//先判断整个文件是否已经上传过了,如果是,则告诉前端跳过上传,实现秒传
if(FileInfoUtils.fileExists(file)) {
ur.setSkipUpload(true);
ur.setLocation(file);
response.setStatus(HttpServletResponse.SC_OK);
ur.setMessage("完整文件已存在,直接跳过上传,实现秒传");
return genericsSuccess(ur);
}
//如果完整文件不存在,则去数据库判断当前哪些文件块已经上传过了,把结果告诉前端,跳过这些文件块的上传,实现断点续传
ArrayList<Integer> list = chunkService.checkChunk(chunk);
if (list !=null && list.size() > 0) {
ur.setSkipUpload(false);
ur.setUploadedChunks(list);
response.setStatus(HttpServletResponse.SC_OK);
ur.setMessage("部分文件块已存在,继续上传剩余文件块,实现断点续传");
return genericsSuccess(ur);
}
return genericsSuccess(ur);
}

@PostMapping("/mergeFile")
public GenericsAjaxResult<TFileInfo> mergeFile(@RequestBody TFileInfoVO fileInfoVO){
String rlt = "FAILURE";
//前端组件参数转换为model对象
TFileInfo fileInfo = new TFileInfo();
fileInfo.setFilename(fileInfoVO.getName());
fileInfo.setIdentifier(fileInfoVO.getUniqueIdentifier());
fileInfo.setId(fileInfoVO.getId());
fileInfo.setTotalSize(fileInfoVO.getSize());
fileInfo.setRefProjectId(fileInfoVO.getRefProjectId());
//进行文件的合并操作
String filename = fileInfo.getFilename();
String file = uploadFolder + "/" + fileInfo.getIdentifier() + "/" + filename;
String folder = uploadFolder + "/" + fileInfo.getIdentifier();
String fileSuccess = FileInfoUtils.merge(file, folder, filename);
fileInfo.setLocation(file);
//文件合并成功后,保存记录至数据库
if("200".equals(fileSuccess)) {
if(fileInfoService.addFileInfo(fileInfo) > 0) rlt = "SUCCESS";
}

//如果已经存在,则判断是否同一个项目,同一个项目的不用新增记录,否则新增
if("300".equals(fileSuccess)) {
List<TFileInfo> tfList = fileInfoService.selectFileByParams(fileInfo);
if(tfList != null) {
if(tfList.size() == 0 || (tfList.size() > 0 && !fileInfo.getRefProjectId().equals(tfList.get(0).getRefProjectId()))) {
if(fileInfoService.addFileInfo(fileInfo) > 0) rlt = "SUCCESS";
}
}
}

return genericsSuccess(fileInfo);
}
/**
* 查询列表
*
* @return ApiResult
*/
@RequestMapping(value = "/selectFileList", method = RequestMethod.GET)
public GenericsAjaxResult<List<TFileInfo>> selectFileList(TFileInfo file){
List<TFileInfo> list = fileInfoService.selectFileList(file);
return genericsSuccess(list);
}
/**
* 下载文件
* @param req
* @param resp
*/
@RequestMapping(value = "/download", method = RequestMethod.GET)
public void download(HttpServletRequest req, HttpServletResponse resp){
String location = req.getParameter("location");
String fileName = req.getParameter("filename");
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
OutputStream fos = null;
try {
bis = new BufferedInputStream(new FileInputStream(location));
fos = resp.getOutputStream();
bos = new BufferedOutputStream(fos);
ServletUtils.setFileDownloadHeader(req, resp, fileName);
int byteRead = 0;
byte[] buffer = new byte[8192];
while ((byteRead = bis.read(buffer, 0, 8192)) != -1) {
bos.write(buffer, 0, byteRead);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.flush();
bis.close();
fos.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 删除
*/
@RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
public GenericsAjaxResult<Integer> deleteFile(@RequestBody TFileInfo tFileInfo ){
int result = fileInfoService.deleteFile(tFileInfo);
return genericsSuccess(result);
}
}

+ 142
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/TChunkInfo.java View File

@@ -0,0 +1,142 @@
package com.ruoyi.platform.domain;

import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;

public class TChunkInfo implements Serializable {

private static final long serialVersionUID = 1L;
private String id;
/**
* 当前文件块,从1开始
*/
private Integer chunkNumber;
/**
* 每块大小
*/
private Long chunkSize;
/**
* 当前分块大小
*/
private Long currentChunkSize;
/**
* 总大小
*/
private Long totalSize;
/**
* 文件标识
*/
private String identifier;
/**
* 文件名
*/
private String filename;
/**
* 相对路径
*/
private String relativePath;
/**
* 总块数
*/
private Integer totalChunks;
/**
* 文件类型
*/
private String type;

/**
* 块内容
*/
private transient MultipartFile upfile;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public Integer getChunkNumber() {
return chunkNumber;
}

public void setChunkNumber(Integer chunkNumber) {
this.chunkNumber = chunkNumber;
}

public Long getChunkSize() {
return chunkSize;
}

public void setChunkSize(Long chunkSize) {
this.chunkSize = chunkSize;
}

public Long getCurrentChunkSize() {
return currentChunkSize;
}

public void setCurrentChunkSize(Long currentChunkSize) {
this.currentChunkSize = currentChunkSize;
}

public Long getTotalSize() {
return totalSize;
}

public void setTotalSize(Long totalSize) {
this.totalSize = totalSize;
}

public String getIdentifier() {
return identifier;
}

public void setIdentifier(String identifier) {
this.identifier = identifier;
}

public String getFilename() {
return filename;
}

public void setFilename(String filename) {
this.filename = filename;
}

public String getRelativePath() {
return relativePath;
}

public void setRelativePath(String relativePath) {
this.relativePath = relativePath;
}

public Integer getTotalChunks() {
return totalChunks;
}

public void setTotalChunks(Integer totalChunks) {
this.totalChunks = totalChunks;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public MultipartFile getUpfile() {
return upfile;
}

public void setUpfile(MultipartFile upfile) {
this.upfile = upfile;
}
}

+ 161
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/TFileInfo.java View File

@@ -0,0 +1,161 @@
package com.ruoyi.platform.domain;

import java.io.Serializable;
import java.util.Date;

/**
* 文件模型
* @author JaredJia
*
*/
public class TFileInfo implements Serializable {

private static final long serialVersionUID = 6969462437970901728L;

//附件编号
private String id;

//附件名称
private String filename;
private String nameSearch;

//附件MD5标识
private String identifier;

//附件总大小
private Long totalSize;
private String totalSizeName;

//附件类型
private String type;

//附件存储地址
private String location;
//删除标志
private String delFlag;
//文件所属目标(项目、学生、档案等,预留字段)
private String refProjectId;
//上传人
private String uploadBy;

//上传时间
private Date uploadTime;
private String uploadTimeString;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getFilename() {
return filename;
}

public void setFilename(String filename) {
this.filename = filename;
}

public String getNameSearch() {
return nameSearch;
}

public void setNameSearch(String nameSearch) {
this.nameSearch = nameSearch;
}

public String getIdentifier() {
return identifier;
}

public void setIdentifier(String identifier) {
this.identifier = identifier;
}

public Long getTotalSize() {
return totalSize;
}

public void setTotalSize(Long totalSize) {
this.totalSize = totalSize;
if(1024*1024 > this.totalSize && this.totalSize >= 1024 ) {
this.totalSizeName = String.format("%.2f",this.totalSize.doubleValue()/1024) + "KB";
}else if(1024*1024*1024 > this.totalSize && this.totalSize >= 1024*1024 ) {
this.totalSizeName = String.format("%.2f",this.totalSize.doubleValue()/(1024*1024)) + "MB";
}else if(this.totalSize >= 1024*1024*1024 ) {
this.totalSizeName = String.format("%.2f",this.totalSize.doubleValue()/(1024*1024*1024)) + "GB";
}else {
this.totalSizeName = this.totalSize.toString() + "B";
}
}

public String getTotalSizeName() {
return totalSizeName;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getLocation() {
return location;
}

public void setLocation(String location) {
this.location = location;
}

public String getDelFlag() {
return delFlag;
}

public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}

public String getRefProjectId() {
return refProjectId;
}

public void setRefProjectId(String refProjectId) {
this.refProjectId = refProjectId;
}

public String getUploadBy() {
return uploadBy;
}

public void setUploadBy(String uploadBy) {
this.uploadBy = uploadBy;
}

public Date getUploadTime() {
return uploadTime;
}

public void setUploadTime(Date uploadTime) {
this.uploadTime = uploadTime;
}

public String getUploadTimeString() {
return uploadTimeString;
}

public void setUploadTimeString(String uploadTimeString) {
this.uploadTimeString = uploadTimeString;
}
}

+ 69
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/UploadResult.java View File

@@ -0,0 +1,69 @@
package com.ruoyi.platform.domain;

import java.io.Serializable;
import java.util.ArrayList;

/**
* 文件上传结果
* @author JaredJia
*
*/
public class UploadResult implements Serializable {
private static final long serialVersionUID = -9000695051292877324L;

//是否跳过上传(已上传的可以直接跳过,达到秒传的效果)
private boolean skipUpload;

//已经上传的文件块编号,可以跳过,断点续传
private ArrayList<Integer> uploadedChunks;

//返回结果码
private String status;

//返回结果信息
private String message;

//已上传完整附件的地址
private String location;

public boolean isSkipUpload() {
return skipUpload;
}

public void setSkipUpload(boolean skipUpload) {
this.skipUpload = skipUpload;
}

public ArrayList<Integer> getUploadedChunks() {
return uploadedChunks;
}

public void setUploadedChunks(ArrayList<Integer> uploadedChunks) {
this.uploadedChunks = uploadedChunks;
}

public String getStatus() {
return status;
}

public void setStatus(String status) {
this.status = status;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getLocation() {
return location;
}

public void setLocation(String location) {
this.location = location;
}
}

+ 23
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/TChunkInfoDao.java View File

@@ -0,0 +1,23 @@
package com.ruoyi.platform.mapper;

import com.ruoyi.platform.domain.TChunkInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.ArrayList;

@Mapper
public interface TChunkInfoDao {
int deleteByPrimaryKey(String id);

int insert(TChunkInfo record);

int insertSelective(TChunkInfo record);

TChunkInfo selectByPrimaryKey(String id);

int updateByPrimaryKeySelective(TChunkInfo record);

int updateByPrimaryKey(TChunkInfo record);
ArrayList<Integer> selectChunkNumbers(TChunkInfo record);
}

+ 27
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/mapper/TFileInfoDao.java View File

@@ -0,0 +1,27 @@
package com.ruoyi.platform.mapper;

import com.ruoyi.platform.domain.TFileInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface TFileInfoDao {
int deleteByPrimaryKey(String id);

int insert(TFileInfo record);

int insertSelective(TFileInfo record);

TFileInfo selectByPrimaryKey(String id);

int updateByPrimaryKeySelective(TFileInfo record);

int updateByPrimaryKey(TFileInfo record);

List<TFileInfo> selectFileByParams(TFileInfo fileInfo);
List<TFileInfo> selectFileList(TFileInfo fileInfo);

}

+ 36
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/ChunkService.java View File

@@ -0,0 +1,36 @@
package com.ruoyi.platform.service;


import com.ruoyi.platform.domain.TChunkInfo;

import java.util.ArrayList;

/**
* 文件块处理
* @author JaredJia
*
*/
public interface ChunkService {
/**
* 保存文件块
*
* @param chunk
*/
public int saveChunk(TChunkInfo chunk);

/**
* 检查文件块是否存在
*
* @param identifier
* @param chunkNumber
* @return
*/
boolean checkChunk(String identifier, Integer chunkNumber);
/**
* 查询哪些文件块已经上传
* @param chunk
* @return
*/
public ArrayList<Integer> checkChunk(TChunkInfo chunk);
}

+ 29
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/FileInfoService.java View File

@@ -0,0 +1,29 @@
package com.ruoyi.platform.service;


import com.ruoyi.platform.domain.TFileInfo;

import java.util.List;

public interface FileInfoService {
public int addFileInfo(TFileInfo fileInfo);
public List<TFileInfo> selectFileByParams(TFileInfo fileInfo);
/**
* 查询
*
* @param File 查询条件
* @return List
*/
List<TFileInfo> selectFileList(TFileInfo file);
/**
* 删除
* @param TFileInfo
* @return
*/
int deleteFile(TFileInfo tFileInfo);
}

+ 34
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ChunkServiceImpl.java View File

@@ -0,0 +1,34 @@
package com.ruoyi.platform.service.impl;

import com.ruoyi.platform.domain.TChunkInfo;
import com.ruoyi.platform.mapper.TChunkInfoDao;
import com.ruoyi.platform.service.ChunkService;
import com.ruoyi.platform.utils.SnowflakeIdWorker;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;

@Service
public class ChunkServiceImpl implements ChunkService {

@Resource
TChunkInfoDao tChunkInfoMapper;
@Override
public int saveChunk(TChunkInfo chunk) {
chunk.setId(SnowflakeIdWorker.getUUID()+ SnowflakeIdWorker.getUUID());
return tChunkInfoMapper.insertSelective(chunk);
}

@Override
public ArrayList<Integer> checkChunk(TChunkInfo chunk) {
return tChunkInfoMapper.selectChunkNumbers(chunk);
}

@Override
public boolean checkChunk(String identifier, Integer chunkNumber) {
return false;
}

}

+ 46
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/FileInfoServiceImpl.java View File

@@ -0,0 +1,46 @@
package com.ruoyi.platform.service.impl;

import com.ruoyi.platform.domain.TFileInfo;
import com.ruoyi.platform.mapper.TFileInfoDao;
import com.ruoyi.platform.service.FileInfoService;
import com.ruoyi.platform.utils.SnowflakeIdWorker;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
* 文件处理类
* @author JaredJia
*
*/
@Service
public class FileInfoServiceImpl implements FileInfoService {

@Resource
TFileInfoDao tFileInfoMapper;
@Override
public int addFileInfo(TFileInfo fileInfo) {
fileInfo.setId(SnowflakeIdWorker.getUUID()+SnowflakeIdWorker.getUUID());
return tFileInfoMapper.insertSelective(fileInfo);
}
@Override
public List<TFileInfo> selectFileByParams(TFileInfo fileInfo) {
return tFileInfoMapper.selectFileByParams(fileInfo);
}
@Override
public List<TFileInfo> selectFileList(TFileInfo file) {
return tFileInfoMapper.selectFileList(file);
}

@Override
public int deleteFile(TFileInfo tFileInfo) {
TFileInfo t = new TFileInfo();
t.setId(tFileInfo.getId());
t.setDelFlag("1");
return tFileInfoMapper.updateByPrimaryKeySelective(t);
}
}

+ 97
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/FileInfoUtils.java View File

@@ -0,0 +1,97 @@
package com.ruoyi.platform.utils;

import com.ruoyi.platform.domain.TChunkInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.*;

/**
* 文件工具类
* @author JaredJia
*
*/
public class FileInfoUtils {
private final static Logger logger = LoggerFactory.getLogger(FileInfoUtils.class);

public static String generatePath(String uploadFolder, TChunkInfo chunk) {
StringBuilder sb = new StringBuilder();
sb.append(uploadFolder).append("/").append(chunk.getIdentifier());
//判断uploadFolder/identifier 路径是否存在,不存在则创建
if (!Files.isWritable(Paths.get(sb.toString()))) {
logger.info("path not exist,create path: {}", sb.toString());
try {
Files.createDirectories(Paths.get(sb.toString()));
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}

return sb.append("/")
.append(chunk.getFilename())
.append("-")
.append(chunk.getChunkNumber()).toString();
}

/**
* 文件合并
*
* @param targetFile
* @param folder
*/
public static String merge(String file, String folder, String filename){
//默认合并成功
String rlt = "200";
try {
//先判断文件是否存在
if(fileExists(file)) {
//文件已存在
rlt = "300";
}else {
//不存在的话,进行合并
Files.createFile(Paths.get(file));
Files.list(Paths.get(folder))
.filter(path -> !path.getFileName().toString().equals(filename))
.sorted((o1, o2) -> {
String p1 = o1.getFileName().toString();
String p2 = o2.getFileName().toString();
int i1 = p1.lastIndexOf("-");
int i2 = p2.lastIndexOf("-");
return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
})
.forEach(path -> {
try {
//以追加的形式写入文件
Files.write(Paths.get(file), Files.readAllBytes(path), StandardOpenOption.APPEND);
//合并后删除该块
Files.delete(path);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
});
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
//合并失败
rlt = "400";
}
return rlt;
}
/**
* 根据文件的全路径名判断文件是否存在
* @param file
* @return
*/
public static boolean fileExists(String file) {
boolean fileExists = false;
Path path = Paths.get(file);
fileExists = Files.exists(path,new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
return fileExists;
}
}

+ 41
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ServletUtils.java View File

@@ -0,0 +1,41 @@
package com.ruoyi.platform.utils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;

/**
* Servlet工具类
*
* @author JaredJia
*/
public class ServletUtils {

/**
* 文件下载时用于写文件头部信息
* @param request http请求
* @param response http响应
* @param fileName 文件名
*/
public static void setFileDownloadHeader(HttpServletRequest request,
HttpServletResponse response, String fileName) {
try {
String encodedFileName = null;
String agent = request.getHeader("USER-AGENT");
if (null != agent && -1 != agent.indexOf("MSIE")) {
encodedFileName = URLEncoder.encode(fileName, "UTF-8");
} else if (null != agent && -1 != agent.indexOf("Mozilla")) {
encodedFileName = new String(fileName.getBytes("UTF-8"),
"iso-8859-1");
} else {
encodedFileName = URLEncoder.encode(fileName, "UTF-8");
}

response.setHeader("Content-Disposition", "attachment; filename=\""
+ encodedFileName + "\"");
} catch (Exception e) {
e.printStackTrace();
}
}

}

+ 149
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/SnowflakeIdWorker.java View File

@@ -0,0 +1,149 @@
package com.ruoyi.platform.utils;

/**
* 雪花算法生成uuid
*
* @author JaredJia
*/
public class SnowflakeIdWorker {

// ==============================Fields===========================================
/** 开始时间截 (2015-01-01) */
private final long twepoch = 1420041600000L;

/** 机器id所占的位数 */
private final long workerIdBits = 5L;

/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;

/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

/** 支持的最大数据标识id,结果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

/** 序列在id中占的位数 */
private final long sequenceBits = 12L;

/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;

/** 数据标识id向左移17位(12+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;

/** 时间截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);

/** 工作机器ID(0~31) */
private long workerId;

/** 数据中心ID(0~31) */
private long datacenterId;

/** 毫秒内序列(0~4095) */
private long sequence = 0L;

/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;

// ==============================Constructors=====================================
/**
* 构造函数
*
* @param workerId
* 工作ID (0~31)
* @param datacenterId
* 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}

// ==============================Methods==========================================
/**
* 获得下一个ID (该方法是线程安全的)
*
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = timeGen();

// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}

// 如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
// 毫秒内序列溢出
if (sequence == 0) {
// 阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
// 时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}

// 上次生成ID的时间截
lastTimestamp = timestamp;

// 移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}

/**
* 阻塞到下一个毫秒,直到获得新的时间戳
*
* @param lastTimestamp
* 上次生成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}

/**
* 返回以毫秒为单位的当前时间
*
* @return 当前时间(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}

/**
* 获取UUID
*
* @return
*/
public static String getUUID() {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
long id = idWorker.nextId();
return String.valueOf(id);
}

}

+ 92
- 0
ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/TFileInfoVO.java View File

@@ -0,0 +1,92 @@
package com.ruoyi.platform.vo;

import java.io.Serializable;

/**
* 文件模型参数
* @author JaredJia
*
*/
public class TFileInfoVO implements Serializable {

private static final long serialVersionUID = -668666237985927833L;

//附件编号
private String id;
//附件类型
private String fileType;
//附件名称
private String name;
//附件总大小
private Long size;
//附件地址
private String relativePath;

//附件MD5标识
private String uniqueIdentifier;

//附件所属项目ID
private String refProjectId;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getFileType() {
return fileType;
}

public void setFileType(String fileType) {
this.fileType = fileType;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Long getSize() {
return size;
}

public void setSize(Long size) {
this.size = size;
}

public String getRelativePath() {
return relativePath;
}

public void setRelativePath(String relativePath) {
this.relativePath = relativePath;
}

public String getUniqueIdentifier() {
return uniqueIdentifier;
}

public void setUniqueIdentifier(String uniqueIdentifier) {
this.uniqueIdentifier = uniqueIdentifier;
}

public String getRefProjectId() {
return refProjectId;
}

public void setRefProjectId(String refProjectId) {
this.refProjectId = refProjectId;
}
}

+ 150
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/TChunkInfoMapper.xml View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.platform.mapper.TChunkInfoDao">
<resultMap id="BaseResultMap" type="com.ruoyi.platform.domain.TChunkInfo">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="chunk_number" jdbcType="DECIMAL" property="chunkNumber" />
<result column="chunk_size" jdbcType="DECIMAL" property="chunkSize" />
<result column="current_chunkSize" jdbcType="DECIMAL" property="currentChunkSize" />
<result column="identifier" jdbcType="VARCHAR" property="identifier" />
<result column="filename" jdbcType="VARCHAR" property="filename" />
<result column="relative_path" jdbcType="VARCHAR" property="relativePath" />
<result column="total_chunks" jdbcType="DECIMAL" property="totalChunks" />
<result column="type" jdbcType="DECIMAL" property="type" />
</resultMap>
<sql id="Base_Column_List">
id, chunk_number, chunk_size, current_chunkSize, identifier, filename, relative_path,
total_chunks, type
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_chunk_info
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from t_chunk_info
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert" parameterType="com.ruoyi.platform.domain.TChunkInfo">
insert into t_chunk_info (id, chunk_number, chunk_size,
current_chunkSize, identifier, filename,
relative_path, total_chunks, type
)
values (#{id,jdbcType=VARCHAR}, #{chunkNumber,jdbcType=DECIMAL}, #{chunkSize,jdbcType=DECIMAL},
#{currentChunkSize,jdbcType=DECIMAL}, #{identifier,jdbcType=VARCHAR}, #{filename,jdbcType=VARCHAR},
#{relativePath,jdbcType=VARCHAR}, #{totalChunks,jdbcType=DECIMAL}, #{type,jdbcType=DECIMAL}
)
</insert>
<insert id="insertSelective" parameterType="com.ruoyi.platform.domain.TChunkInfo">
insert into t_chunk_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="chunkNumber != null">
chunk_number,
</if>
<if test="chunkSize != null">
chunk_size,
</if>
<if test="currentChunkSize != null">
current_chunkSize,
</if>
<if test="identifier != null">
identifier,
</if>
<if test="filename != null">
filename,
</if>
<if test="relativePath != null">
relative_path,
</if>
<if test="totalChunks != null">
total_chunks,
</if>
<if test="type != null">
type,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="chunkNumber != null">
#{chunkNumber,jdbcType=DECIMAL},
</if>
<if test="chunkSize != null">
#{chunkSize,jdbcType=DECIMAL},
</if>
<if test="currentChunkSize != null">
#{currentChunkSize,jdbcType=DECIMAL},
</if>
<if test="identifier != null">
#{identifier,jdbcType=VARCHAR},
</if>
<if test="filename != null">
#{filename,jdbcType=VARCHAR},
</if>
<if test="relativePath != null">
#{relativePath,jdbcType=VARCHAR},
</if>
<if test="totalChunks != null">
#{totalChunks,jdbcType=DECIMAL},
</if>
<if test="type != null">
#{type,jdbcType=DECIMAL},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.ruoyi.platform.domain.TChunkInfo">
update t_chunk_info
<set>
<if test="chunkNumber != null">
chunk_number = #{chunkNumber,jdbcType=DECIMAL},
</if>
<if test="chunkSize != null">
chunk_size = #{chunkSize,jdbcType=DECIMAL},
</if>
<if test="currentChunkSize != null">
current_chunkSize = #{currentChunkSize,jdbcType=DECIMAL},
</if>
<if test="identifier != null">
identifier = #{identifier,jdbcType=VARCHAR},
</if>
<if test="filename != null">
filename = #{filename,jdbcType=VARCHAR},
</if>
<if test="relativePath != null">
relative_path = #{relativePath,jdbcType=VARCHAR},
</if>
<if test="totalChunks != null">
total_chunks = #{totalChunks,jdbcType=DECIMAL},
</if>
<if test="type != null">
type = #{type,jdbcType=DECIMAL},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="com.ruoyi.platform.domain.TChunkInfo">
update t_chunk_info
set chunk_number = #{chunkNumber,jdbcType=DECIMAL},
chunk_size = #{chunkSize,jdbcType=DECIMAL},
current_chunkSize = #{currentChunkSize,jdbcType=DECIMAL},
identifier = #{identifier,jdbcType=VARCHAR},
filename = #{filename,jdbcType=VARCHAR},
relative_path = #{relativePath,jdbcType=VARCHAR},
total_chunks = #{totalChunks,jdbcType=DECIMAL},
type = #{type,jdbcType=DECIMAL}
where id = #{id,jdbcType=VARCHAR}
</update>
<select id="selectChunkNumbers" parameterType="com.ruoyi.platform.domain.TChunkInfo" resultType="int">
select
chunk_number
from t_chunk_info
where identifier = #{identifier,jdbcType=VARCHAR}
and filename = #{filename,jdbcType=VARCHAR}
</select>
</mapper>

+ 178
- 0
ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/TFileInfoMapper.xml View File

@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.platform.mapper.TFileInfoDao">
<resultMap id="BaseResultMap" type="com.ruoyi.platform.domain.TFileInfo">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="filename" jdbcType="VARCHAR" property="filename" />
<result column="identifier" jdbcType="VARCHAR" property="identifier" />
<result column="type" jdbcType="VARCHAR" property="type" />
<result column="total_size" jdbcType="DECIMAL" property="totalSize" />
<result column="location" jdbcType="VARCHAR" property="location" />
<result column="del_flag" jdbcType="VARCHAR" property="delFlag" />
<result column="ref_project_id" jdbcType="VARCHAR" property="refProjectId" />
<result column="upload_by" jdbcType="VARCHAR" property="uploadBy" />
<result column="upload_time" jdbcType="TIMESTAMP" property="uploadTime" />
</resultMap>
<sql id="Base_Column_List">
id, filename, identifier, type, total_size, location, del_flag, ref_project_id, upload_by,
upload_time
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_file_info
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from t_file_info
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert" parameterType="com.ruoyi.platform.domain.TFileInfo">
insert into t_file_info (id, filename, identifier,
type, total_size, location,
del_flag, ref_project_id, upload_by,
upload_time)
values (#{id,jdbcType=VARCHAR}, #{filename,jdbcType=VARCHAR}, #{identifier,jdbcType=VARCHAR},
#{type,jdbcType=VARCHAR}, #{totalSize,jdbcType=DECIMAL}, #{location,jdbcType=VARCHAR},
#{delFlag,jdbcType=VARCHAR}, #{refProjectId,jdbcType=VARCHAR}, #{uploadBy,jdbcType=VARCHAR},
#{uploadTime,jdbcType=TIMESTAMP})
</insert>
<insert id="insertSelective" parameterType="com.ruoyi.platform.domain.TFileInfo">
insert into t_file_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="filename != null">
filename,
</if>
<if test="identifier != null">
identifier,
</if>
<if test="type != null">
type,
</if>
<if test="totalSize != null">
total_size,
</if>
<if test="location != null">
location,
</if>
<if test="delFlag != null">
del_flag,
</if>
<if test="refProjectId != null">
ref_project_id,
</if>
<if test="uploadBy != null">
upload_by,
</if>
<if test="uploadTime != null">
upload_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="filename != null">
#{filename,jdbcType=VARCHAR},
</if>
<if test="identifier != null">
#{identifier,jdbcType=VARCHAR},
</if>
<if test="type != null">
#{type,jdbcType=VARCHAR},
</if>
<if test="totalSize != null">
#{totalSize,jdbcType=DECIMAL},
</if>
<if test="location != null">
#{location,jdbcType=VARCHAR},
</if>
<if test="delFlag != null">
#{delFlag,jdbcType=VARCHAR},
</if>
<if test="refProjectId != null">
#{refProjectId,jdbcType=VARCHAR},
</if>
<if test="uploadBy != null">
#{uploadBy,jdbcType=VARCHAR},
</if>
<if test="uploadTime != null">
#{uploadTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.ruoyi.platform.domain.TFileInfo">
update t_file_info
<set>
<if test="filename != null">
filename = #{filename,jdbcType=VARCHAR},
</if>
<if test="identifier != null">
identifier = #{identifier,jdbcType=VARCHAR},
</if>
<if test="type != null">
type = #{type,jdbcType=VARCHAR},
</if>
<if test="totalSize != null">
total_size = #{totalSize,jdbcType=DECIMAL},
</if>
<if test="location != null">
location = #{location,jdbcType=VARCHAR},
</if>
<if test="delFlag != null">
del_flag = #{delFlag,jdbcType=VARCHAR},
</if>
<if test="refProjectId != null">
ref_project_id = #{refProjectId,jdbcType=VARCHAR},
</if>
<if test="uploadBy != null">
upload_by = #{uploadBy,jdbcType=VARCHAR},
</if>
<if test="uploadTime != null">
upload_time = #{uploadTime,jdbcType=TIMESTAMP},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="com.ruoyi.platform.domain.TFileInfo">
update t_file_info
set filename = #{filename,jdbcType=VARCHAR},
identifier = #{identifier,jdbcType=VARCHAR},
type = #{type,jdbcType=VARCHAR},
total_size = #{totalSize,jdbcType=DECIMAL},
location = #{location,jdbcType=VARCHAR},
del_flag = #{delFlag,jdbcType=VARCHAR},
ref_project_id = #{refProjectId,jdbcType=VARCHAR},
upload_by = #{uploadBy,jdbcType=VARCHAR},
upload_time = #{uploadTime,jdbcType=TIMESTAMP}
where id = #{id,jdbcType=VARCHAR}
</update>
<select id="selectFileByParams" parameterType="com.ruoyi.platform.domain.TFileInfo" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_file_info
where filename = #{filename,jdbcType=VARCHAR}
and identifier = #{identifier,jdbcType=VARCHAR}
</select>
<select id="selectFileList" resultMap="BaseResultMap" parameterType="com.ruoyi.platform.domain.TFileInfo">
SELECT
id ,
total_size,
filename ,
location,
identifier,
date_format(upload_time,'%Y-%m-%d-%T') AS uploadTimeString
FROM T_FILE_INFO
WHERE del_flag = '0'
<if test="filename != null">
and filename = #{nameSearch,jdbcType=VARCHAR}
</if>
ORDER BY UPLOAD_TIME
</select>
</mapper>

Loading…
Cancel
Save