• 基于Ruoyi和WebUploader的统一附件管理扩展(下)


    目录

    背景

    一、数据库定义

    1、目的

    2、数据库物理表设计

    二、JAVA后台服务定义

    1、实体类定义

    2、数据服务和业务层处理

    3、控制层定义

     三、总结


    背景

           在之前的博文中,简单介绍了如何扩展Ruoyi的大附件上传及统一管理一篇,原文地址:基于Ruoyi和WebUploader的统一附件管理扩展(上)。之前的博文主要集中在前台的讲解,前台主要是围绕WebUploader组件来说明,对应的后台处理没有仔细讲解。本文作为下篇,主要围绕大附件上传的后台设计及实现,通过相关的UML建模,具体阐述后台是如何进行对应的处理。包括断点续传,文件重复判断等特殊功能的实例讲解。希望对你在项目中的使用有所启发。

    一、数据库定义

    1、目的

           定义数据实体的目的是为了可以在各业务中通用,提供统一的组件及封装,同时为了可以在文件处理时满足实时对文件是否重复上传做校验,因此有必要采用数据库的方式进行存储。当然,这里的数据存储未必要使用关系型数据库,使用非关系型数据库,比如MongoDB也是可以的。只需要达到我们的目的即可。

    2、数据库物理表设计

           设计统一附件管理模块,不仅要统一文件上传的操作界面,同时要提供统一的API可以对附件进行检索,因而要将一个业务表的表名传递到附件表中。同时为了,防止一张业务表在不同状态下也可以拥有不同的附件,额外增加一个业务类型的字段。比如一个流程审核的业务,在流程新建阶段关联的附件和审批中关联的附件可以是不一样的。由此字段可以区分开,为了在文件上传过程中加速,也避免减少同一份文件反复上传到磁盘中,造成不必要的资源浪费,因此非常有必要对资源进行判重。关于文件判重,可以参考之前的博文:java文件上传判重姿势浅谈,这里有比较详细的说明。根据上述的需求,可以得到一个数据库物理表的表结构:

            这是一份基于postgresql数据库的表结构,同理在mysql或者其它的数据库下,是一样的,只是数据类型需要稍微修改一下而已。这里将表结构对应的sql文件分享一下:

    1. -- ----------------------------
    2. -- Table structure for biz_file
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS "biz_file";
    5. CREATE TABLE "biz_file" (
    6. "id" int8 NOT NULL,
    7. "f_id" varchar(100) ,
    8. "b_id" varchar(100) ,
    9. "f_type" varchar(30) NOT NULL,
    10. "f_name" varchar(512) NOT NULL,
    11. "f_desc" varchar(512) ,
    12. "f_state" int2,
    13. "f_size" int8 NOT NULL,
    14. "f_path" varchar(1024) NOT NULL,
    15. "table_name" varchar(255) ,
    16. "md5code" varchar(255) ,
    17. "directory" varchar(1024) ,
    18. "create_by" varchar(64) ,
    19. "create_time" timestamp(6),
    20. "update_by" varchar(64) ,
    21. "update_time" timestamp(6),
    22. "biz_type" varchar(30)
    23. )
    24. ;
    25. COMMENT ON COLUMN "biz_file"."id" IS '主键';
    26. COMMENT ON COLUMN "biz_file"."f_id" IS '文件id';
    27. COMMENT ON COLUMN "biz_file"."b_id" IS '业务id';
    28. COMMENT ON COLUMN "biz_file"."f_type" IS '文件类型';
    29. COMMENT ON COLUMN "biz_file"."f_name" IS '名称';
    30. COMMENT ON COLUMN "biz_file"."f_desc" IS '文件描述';
    31. COMMENT ON COLUMN "biz_file"."f_state" IS '文件状态';
    32. COMMENT ON COLUMN "biz_file"."f_size" IS '文件大小';
    33. COMMENT ON COLUMN "biz_file"."f_path" IS '文件路径';
    34. COMMENT ON COLUMN "biz_file"."table_name" IS '业务表名';
    35. COMMENT ON COLUMN "biz_file"."md5code" IS 'md5code';
    36. COMMENT ON COLUMN "biz_file"."directory" IS '文件目录';
    37. COMMENT ON COLUMN "biz_file"."create_by" IS '创建人';
    38. COMMENT ON COLUMN "biz_file"."create_time" IS '创建时间';
    39. COMMENT ON COLUMN "biz_file"."update_by" IS '更新人';
    40. COMMENT ON COLUMN "biz_file"."update_time" IS '更新时间';
    41. COMMENT ON COLUMN "biz_file"."biz_type" IS '业务类型';
    42. COMMENT ON TABLE "biz_file" IS '系统附件信息表,用于保存文件上传信息';
    43. -- ----------------------------
    44. -- Primary Key structure for table biz_file
    45. -- ----------------------------
    46. ALTER TABLE "biz_file" ADD CONSTRAINT "pk_biz_file" PRIMARY KEY ("id");

    二、JAVA后台服务定义

    1、实体类定义

           实体类主要用于定义跟数据库表相互映射的对象信息,使用sql语句来操作数据库信息。示例中的代码均需要配置lombok来简化相关类的开发工作量。

    1. package com.hngtghy.project.webupload.domain;
    2. import com.baomidou.mybatisplus.annotation.TableField;
    3. import com.baomidou.mybatisplus.annotation.TableName;
    4. import com.hngtghy.framework.web.domain.BaseEntity;
    5. import lombok.AllArgsConstructor;
    6. import lombok.Getter;
    7. import lombok.NoArgsConstructor;
    8. import lombok.Setter;
    9. import lombok.ToString;
    10. @TableName("biz_file")
    11. @NoArgsConstructor
    12. @AllArgsConstructor
    13. @Setter
    14. @Getter
    15. @ToString
    16. public class FileEntity extends BaseEntity {
    17. private static final long serialVersionUID = 1L;
    18. private Long id;
    19. @TableField(value = "f_id")
    20. private String fid;
    21. @TableField(value = "b_id")
    22. private String bid;
    23. @TableField(value = "f_type")
    24. private String type;
    25. @TableField(value = "f_name")
    26. private String name;
    27. @TableField(value = "f_desc")
    28. private String desc;
    29. @TableField(value = "f_state")
    30. private Integer state;
    31. @TableField(value = "f_size")
    32. private Long size;
    33. @TableField(value = "f_path")
    34. private String path;
    35. @TableField(value = "table_name")
    36. private String tablename = "temp_table";
    37. private String md5code;
    38. private String directory;
    39. @TableField(value = "biz_type")
    40. private String bizType;
    41. }

    2、数据服务和业务层处理

            数据服务,主要基于mybatis-plus来进行增删改查操作,而业务层包含了基础的业务封装,除了调用数据服务外,还有一些额外的业务处理操作。

    1. package com.hngtghy.project.webupload.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.hngtghy.project.webupload.domain.FileEntity;
    4. public interface FileMapper extends BaseMapper {
    5. }

             在服务层中定义了对附件对象的删除、查询、修改、保存的功能方法,以接口的形式进行定义。

    1. package com.hngtghy.project.webupload.service;
    2. import java.util.List;
    3. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    4. import com.baomidou.mybatisplus.extension.service.IService;
    5. import com.hngtghy.project.system.user.domain.User;
    6. import com.hngtghy.project.webupload.domain.FileEntity;
    7. public interface IFileService extends IService{
    8. int saveEntity(FileEntity entity) throws Exception;
    9. int updateEntity(FileEntity entity) throws Exception;
    10. int removeByIds(Long [] ids) throws Exception;
    11. int removeByBids(List bids) throws Exception;
    12. FileEntity getOneByFid(String fid) throws Exception;
    13. int removeByFid(String fid) throws Exception;
    14. void deleteErrorFile(User user);
    15. Long findFileSizeByWrapper(QueryWrapper queryWrapper);
    16. List findListByQueryWrapper(QueryWrapper queryWrapper);
    17. FileEntity findByMd5Code(String md5Code) throws Exception;
    18. }

            其具体的实现类如下,这里主要针对在接口定义的方法进行重写,以满足新的业务需求,比较重要的方法是findByMd5Code,通过这个方法到数据库中查询是否有重复的文件,当有重复文件后,将不再重复上传,关键代码如下:

    1. package com.hngtghy.project.webupload.service;
    2. import java.io.File;
    3. import java.io.FileFilter;
    4. import java.sql.PreparedStatement;
    5. import java.util.Arrays;
    6. import java.util.Date;
    7. import java.util.List;
    8. import org.springframework.stereotype.Service;
    9. import org.springframework.transaction.annotation.Propagation;
    10. import org.springframework.transaction.annotation.Transactional;
    11. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    12. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    13. import com.hngtghy.common.utils.StringUtils;
    14. import com.hngtghy.common.utils.security.ShiroUtils;
    15. import com.hngtghy.framework.aspectj.lang.annotation.DataSource;
    16. import com.hngtghy.framework.config.HngtghyConfig;
    17. import com.hngtghy.project.system.user.domain.User;
    18. import com.hngtghy.project.webupload.domain.FileEntity;
    19. import com.hngtghy.project.webupload.mapper.FileMapper;
    20. @Service
    21. public class FileServiceImpl extends ServiceImpl implements IFileService {
    22. @Override
    23. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    24. public int saveEntity(FileEntity entity) throws Exception {
    25. entity.setCreateBy(ShiroUtils.getLoginName());
    26. entity.setCreateTime(new Date());
    27. int result = this.save(entity) ? 1 : 0;
    28. return result;
    29. }
    30. @Override
    31. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    32. public int updateEntity(FileEntity entity) throws Exception {
    33. int result = this.updateById(entity) ? 1 : 0;
    34. return result;
    35. }
    36. @Override
    37. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    38. public int removeByIds(Long[] ids) throws Exception {
    39. List removeList = Arrays.asList(ids);
    40. for (Long id : ids) {
    41. deleteFileOnDiskById(id);
    42. }
    43. int result = this.removeByIds(removeList) ? 1 : 0;
    44. return result;
    45. }
    46. @Override
    47. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    48. public FileEntity getOneByFid(String fid) throws Exception {
    49. QueryWrapper queryWrapper = new QueryWrapper();
    50. queryWrapper.eq("f_id", fid);
    51. FileEntity file = this.getOne(queryWrapper);
    52. return file;
    53. }
    54. @Override
    55. public int removeByFid(String fid) throws Exception {
    56. QueryWrapper queryWrapper = new QueryWrapper();
    57. queryWrapper.eq("f_id", fid);
    58. deleteFileOnDiskByFid(fid);
    59. int result = this.remove(queryWrapper) ? 1 : 0;
    60. return result;
    61. }
    62. private void deleteFileOnDiskByFid(String fid) throws Exception {
    63. FileEntity file = this.getOneByFid(fid);
    64. String file_path = file.getPath();
    65. if(!sharedFile(file_path)){
    66. this.deleteFileOnDisk(file_path);
    67. }
    68. }
    69. private void deleteFileOnDiskById(Long id) throws Exception {
    70. FileEntity file = this.getById(id);
    71. String file_path = file.getPath();
    72. if(!sharedFile(file_path)){
    73. this.deleteFileOnDisk(file.getPath());
    74. }
    75. }
    76. private boolean sharedFile(String path){
    77. QueryWrapper queryWrapper = new QueryWrapper();
    78. queryWrapper.eq("f_path", path);
    79. queryWrapper.eq("f_state", 1);
    80. List files = this.list(queryWrapper);
    81. if (files != null && files.size() > 1) {
    82. return true;
    83. }
    84. return false;
    85. }
    86. private void deleteFileOnDisk(String path) throws Exception {
    87. File file = new File(HngtghyConfig.getProfile() + "/" + path);
    88. file.deleteOnExit();
    89. if (file.isFile() && file.exists()) {
    90. file.delete();
    91. }
    92. }
    93. /**
    94. * 删除失败文件、未绑定文件
    95. * @author
    96. * @date
    97. */
    98. public void deleteErrorFile(User user){
    99. try{
    100. String path = HngtghyConfig.getProfile() + "/";
    101. QueryWrapper queryWrapper = new QueryWrapper();
    102. queryWrapper.eq("f_state", 0);
    103. queryWrapper.lt("create_time", "now() - INTERVAL '3 days'");//三天前的失败文件
    104. List files = this.list(queryWrapper);
    105. if (files != null && files.size() > 0) {
    106. this.remove(queryWrapper);
    107. for(FileEntity file:files){
    108. deleteFileOnDiskById(file.getId());
    109. }
    110. }
    111. queryWrapper = new QueryWrapper();
    112. queryWrapper.eq("linked", 0);
    113. queryWrapper.lt("create_time", "now() - INTERVAL '3 days'");
    114. files = this.list(queryWrapper);
    115. if (files != null && files.size() > 0) {
    116. this.remove(queryWrapper);
    117. for(FileEntity file:files){
    118. deleteFileOnDiskById(file.getId());
    119. }
    120. }
    121. //三天前的分片临时目录
    122. String save_path = path + (user != null ? user.getUserId() : "unkown");
    123. File directory = new File(save_path);
    124. File[] fileArray = directory.listFiles(new FileFilter() {
    125. @Override
    126. public boolean accept(File pathname) {
    127. long last = pathname.lastModified();
    128. long diff = System.currentTimeMillis() - last;
    129. boolean del = (diff - 24 * 60 * 60 * 1000) > 0;
    130. if (pathname.isDirectory() && del) {
    131. return true;
    132. }
    133. return false;
    134. }
    135. });
    136. for(File dir : fileArray){
    137. dir.delete();
    138. }
    139. }catch(Exception e){
    140. //无需处理该异常
    141. }
    142. }
    143. @Override
    144. public Long findFileSizeByWrapper(QueryWrapper queryWrapper) {
    145. Long result = this.count(queryWrapper);
    146. return result;
    147. }
    148. @Override
    149. public List findListByQueryWrapper(QueryWrapper queryWrapper) {
    150. List result = this.list(queryWrapper);
    151. return result;
    152. }
    153. /**
    154. * @Title: removeByBids
    155. * @Description: 根据bid删除附件
    156. * @param bids
    157. * @return
    158. * @throws Exception
    159. */
    160. @Override
    161. public int removeByBids(List bids) throws Exception {
    162. QueryWrapper paramWrapper = new QueryWrapper<>();
    163. paramWrapper.in("b_id", bids);
    164. List files = this.list(paramWrapper);
    165. int ret = this.remove(paramWrapper)?1:0;
    166. if(files == null || files.size() == 0)
    167. return 0;
    168. for(FileEntity file:files){
    169. String file_path = file.getPath();
    170. if(!sharedFile(file_path)){
    171. this.deleteFileOnDisk(file_path);
    172. }
    173. }
    174. return ret;
    175. }
    176. @Override
    177. public FileEntity findByMd5Code(String md5Code) throws Exception {
    178. QueryWrapper queryWrapper = new QueryWrapper();
    179. queryWrapper.eq("md5code", md5Code);
    180. List list = this.list(queryWrapper);
    181. return StringUtils.isEmpty(list) ? null : list.get(0);
    182. }
    183. }

    3、控制层定义

            控制层主要用于接收前端WebUploader提交过来的请求,同时调用相应的服务后进行响应。关键如下面所示:
     

    1. package com.hngtghy.project.webupload.controller;
    2. import java.io.File;
    3. import java.io.FileFilter;
    4. import java.io.FileInputStream;
    5. import java.io.FileOutputStream;
    6. import java.io.IOException;
    7. import java.io.InputStream;
    8. import java.io.OutputStream;
    9. import java.nio.channels.FileChannel;
    10. import java.util.ArrayList;
    11. import java.util.Arrays;
    12. import java.util.Collections;
    13. import java.util.Comparator;
    14. import java.util.HashSet;
    15. import java.util.List;
    16. import java.util.Set;
    17. import java.util.UUID;
    18. import javax.servlet.http.HttpServletResponse;
    19. import org.springframework.beans.factory.annotation.Autowired;
    20. import org.springframework.context.ApplicationContext;
    21. import org.springframework.stereotype.Controller;
    22. import org.springframework.ui.ModelMap;
    23. import org.springframework.web.bind.annotation.GetMapping;
    24. import org.springframework.web.bind.annotation.PostMapping;
    25. import org.springframework.web.bind.annotation.RequestMapping;
    26. import org.springframework.web.bind.annotation.RequestParam;
    27. import org.springframework.web.bind.annotation.ResponseBody;
    28. import org.springframework.web.multipart.MultipartFile;
    29. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    30. import com.hngtghy.common.utils.StringUtils;
    31. import com.hngtghy.common.utils.file.FileTypeUtils;
    32. import com.hngtghy.framework.config.HngtghyConfig;
    33. import com.hngtghy.framework.event.FileUploadEvent;
    34. import com.hngtghy.framework.web.controller.BaseController;
    35. import com.hngtghy.framework.web.domain.AjaxResult;
    36. import com.hngtghy.framework.web.page.LayerTableDataInfo;
    37. import com.hngtghy.project.system.user.domain.User;
    38. import com.hngtghy.project.webupload.domain.FileEntity;
    39. import com.hngtghy.project.webupload.service.IFileService;
    40. import com.github.pagehelper.util.StringUtil;
    41. /**
    42. * 文件上传相关
    43. * @author wuzuhu
    44. */
    45. @Controller
    46. @RequestMapping("/uploadfile")
    47. public class UploadFileController extends BaseController {
    48. private String prefix = "upload/";
    49. @Autowired
    50. private IFileService fileService;
    51. private final int block_size = 5*1024*1024;
    52. @Autowired
    53. private ApplicationContext applicationContext;
    54. /**
    55. * 文件列表页面
    56. */
    57. @GetMapping("/main")
    58. public String main(ModelMap mmap,String bid,String tablename,String bizType,String multipleMode) {
    59. mmap.put("bid", bid);//业务表id
    60. mmap.put("temp_b_id", UUID.randomUUID().toString());
    61. mmap.put("tablename", tablename);//业务表名
    62. mmap.put("bizType", bizType);//业务类型
    63. mmap.put("multipleMode", multipleMode);//文件多选模式,默认为多选,为空即可。单选需要设置为:single
    64. return prefix + "fileTablePage";
    65. }
    66. /**
    67. * 文件上传进度页面
    68. */
    69. @GetMapping("/process")
    70. public String upload(ModelMap mmap) {
    71. return prefix + "uploadProcessModal";
    72. }
    73. /**
    74. * 文件上传进度页面
    75. */
    76. @GetMapping("/view")
    77. public String view(ModelMap mmap) {
    78. return prefix + "viewFile";
    79. }
    80. @ResponseBody
    81. @PostMapping("/bigUploader")
    82. public AjaxResult bigUploader(String chunk,String chunks,String fid,
    83. @RequestParam("file") MultipartFile multipartFile) {
    84. String path = HngtghyConfig.getProfile() + "/";
    85. try {
    86. FileEntity db_file = fileService.getOneByFid(fid);
    87. if (db_file == null) {
    88. return AjaxResult.error(AjaxResult.Type.WEBUPLOADERROR.value(),"没找到数据库记录");
    89. }
    90. if(db_file.getState() == 1){
    91. return AjaxResult.success();
    92. }
    93. User user = getSysUser();
    94. String save_dir = path + (user != null ? user.getUserId() : "unkown") ;
    95. String chunk_dir = save_dir + "/" + fid ;
    96. boolean sign = chunks != null && Integer.parseInt(chunks) > 0;
    97. String tempmlDir = sign ? chunk_dir : save_dir;
    98. String chunkFilePath = sign ? chunk_dir + "/" + chunk : chunk_dir + "." + db_file.getType();
    99. if(!sign) {
    100. db_file.setState(1);
    101. fileService.updateById(db_file);
    102. }
    103. File tempml = new File(tempmlDir);
    104. if (!tempml.exists()) {
    105. tempml.mkdirs();
    106. }
    107. File chunkFile = new File(chunkFilePath);
    108. multipartFile.transferTo(chunkFile);
    109. return AjaxResult.success();
    110. } catch (Exception e) {
    111. return AjaxResult.error(AjaxResult.Type.WEBUPLOADERROR.value(),"服务端异常");
    112. }
    113. }
    114. @ResponseBody
    115. @PostMapping("/merge")
    116. public AjaxResult merge(String action,String chunks,String chunk,String chunkSize,String temp_b_id,FileEntity fileObj) {
    117. try {
    118. User user = getSysUser();
    119. String fid = fileObj.getFid();
    120. //公共目录
    121. String commonDir = user != null ? String.valueOf(user.getUserId()) : "unkown";
    122. FileEntity db_file = fileService.getOneByFid(fid);
    123. String type = FileTypeUtils.getFileType(fileObj.getName());
    124. String path = HngtghyConfig.getProfile() + "/";
    125. String chunk_dir = path + commonDir + "/" + fid;
    126. //数据库保存目录考虑可迁移 add by wuzuhu on 2022-07-18
    127. String dbPath = commonDir + "/" + fid + "." + type;
    128. if (action.equals("mergeChunks")) {
    129. return mergeChunks(db_file,chunk_dir,chunks,path + dbPath);
    130. }
    131. if (action.equals("checkChunk")) {
    132. String chunkfile_dir = chunk_dir + "/" + chunk;
    133. return checkChunk(chunkfile_dir,chunkSize);
    134. }
    135. if (action.equals("exSendFile")) {
    136. FileEntity md5File = fileService.findByMd5Code(fileObj.getMd5code());
    137. if(null != md5File) {
    138. dbPath = md5File.getPath();
    139. fileObj.setName(md5File.getName());
    140. }
    141. fileObj.setPath(dbPath);
    142. fileObj.setType(type);
    143. fileObj.setBid(temp_b_id);
    144. fileObj.setState(0);
    145. return exSendFile(db_file,path + dbPath,fileObj);
    146. }
    147. } catch (Exception e) {
    148. return AjaxResult.error();
    149. }
    150. return AjaxResult.success();
    151. }
    152. private AjaxResult checkChunk(String chunkfile_dir,String chunkSize) {
    153. File checkFile = new File(chunkfile_dir);
    154. if (checkFile.exists() && checkFile.length() == Integer.parseInt(chunkSize)) {
    155. return AjaxResult.error(2,"文件已存在");
    156. } else {
    157. return AjaxResult.success();
    158. }
    159. }
    160. @SuppressWarnings("resource")
    161. private AjaxResult mergeChunks(FileEntity db_file,String chunk_dir,String chunks,String f_path) throws IOException {
    162. if (db_file == null) {
    163. return AjaxResult.error(AjaxResult.Type.WEBUPLOADERROR.value(), "找不到数据");
    164. }
    165. if (db_file.getState() == 1) {
    166. //未分片文件上传成功合并成功后发布相应事件,各监听器自由监听并执行
    167. applicationContext.publishEvent(new FileUploadEvent(this, db_file));
    168. return AjaxResult.success();
    169. }
    170. if(db_file.getSize() > block_size){
    171. File f = new File(chunk_dir);
    172. File[] fileArray = f.listFiles(new FileFilter() {
    173. @Override
    174. public boolean accept(File pathname) {
    175. if (pathname.isDirectory()) {
    176. return false;
    177. }
    178. return true;
    179. }
    180. });
    181. if (fileArray == null || fileArray.length == 0) {
    182. return AjaxResult.error(AjaxResult.Type.WEBUPLOADERROR.value(), "找不到分片文件");
    183. }
    184. if (StringUtil.isNotEmpty(chunks) && fileArray.length != Integer.parseInt(chunks)) {
    185. return AjaxResult.error(AjaxResult.Type.WEBUPLOADERROR.value(), "分片文件数量错误");
    186. }
    187. List fileList = new ArrayList(Arrays.asList(fileArray));
    188. Collections.sort(fileList, new Comparator() {
    189. @Override
    190. public int compare(File o1, File o2) {
    191. if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
    192. return -1;
    193. }
    194. return 1;
    195. }
    196. });
    197. File outputFile = new File(f_path);
    198. outputFile.createNewFile();
    199. FileChannel outChnnel = new FileOutputStream(outputFile).getChannel();
    200. FileChannel inChannel;
    201. for (File file : fileList) {
    202. inChannel = new FileInputStream(file).getChannel();
    203. inChannel.transferTo(0, inChannel.size(), outChnnel);
    204. inChannel.close();
    205. file.delete();
    206. }
    207. outChnnel.close();
    208. db_file.setState(1);
    209. fileService.updateById(db_file);
    210. File tempFile = new File(chunk_dir);
    211. if (tempFile.isDirectory() && tempFile.exists()) {
    212. tempFile.delete();
    213. }
    214. //分片文件上传成功合并成功后发布相应事件,各监听器自由监听并执行
    215. applicationContext.publishEvent(new FileUploadEvent(this, db_file));
    216. }
    217. return AjaxResult.success();
    218. }
    219. private AjaxResult exSendFile(FileEntity db_file,String f_path,FileEntity fileObj) throws Exception {
    220. if (db_file != null) {
    221. return AjaxResult.success();
    222. }
    223. System.out.println("md5code==>" + fileObj.getMd5code() + "\t f_path=="+ f_path);
    224. System.out.println("fileObj id ==>" + fileObj.getId() + "\t fid===>" + fileObj.getFid());
    225. fileObj.setState(0);
    226. fileService.saveEntity(fileObj);
    227. //执行插入
    228. File file_indisk = new File(f_path);
    229. if (file_indisk.exists() && file_indisk.length() == fileObj.getSize()) {
    230. fileObj.setState(1);
    231. fileService.updateById(fileObj);
    232. //已上传文件上传成功合并成功后发布相应事件,各监听器自由监听并执行
    233. applicationContext.publishEvent(new FileUploadEvent(this, fileObj));
    234. return AjaxResult.error(2,"文件已存在");
    235. } else {
    236. return AjaxResult.success();
    237. }
    238. }
    239. /**
    240. * 删除文件
    241. * @param fid
    242. */
    243. @ResponseBody
    244. @RequestMapping("/delete")
    245. public AjaxResult delete(@RequestParam("ids[]") Long[] ids) throws Exception {
    246. fileService.deleteErrorFile(getSysUser());
    247. int result = fileService.removeByIds(ids);
    248. return result > 0 ? AjaxResult.success() : AjaxResult.error();
    249. }
    250. /**
    251. * 删除文件
    252. * @param fid
    253. */
    254. @ResponseBody
    255. @RequestMapping("/deleteByFid")
    256. public AjaxResult deleteByFid(String fid) throws Exception{
    257. fileService.deleteErrorFile(getSysUser());
    258. int result = fileService.removeByFid(fid);
    259. return result > 0 ? AjaxResult.success() : AjaxResult.error();
    260. }
    261. /**
    262. * 查询文件列表
    263. * @author zlz
    264. * @date 2019年3月19日 下午3:16:32
    265. */
    266. @ResponseBody
    267. @RequestMapping("/list")
    268. public LayerTableDataInfo list(String b_id, String b_ids, String f_id,String name,String tablename,String bizType) throws Exception{
    269. startLayerPage();
    270. QueryWrapper queryWrapper = new QueryWrapper();
    271. queryWrapper.eq("f_state", 1);
    272. if(StringUtils.isNotBlank(b_id)){
    273. queryWrapper.eq("b_id", b_id);
    274. }
    275. if(StringUtils.isNotBlank(b_ids)){
    276. queryWrapper.in("b_id", splitIds(b_ids));
    277. }
    278. if(StringUtils.isNotBlank(f_id)){
    279. queryWrapper.eq("f_id", f_id);
    280. }
    281. if(StringUtils.isNotBlank(name)){
    282. queryWrapper.like("f_name", name);
    283. }
    284. if(StringUtils.isNotBlank(tablename)) {
    285. queryWrapper.eq("table_name", tablename);
    286. }
    287. if(StringUtils.isNotBlank(bizType)){
    288. queryWrapper.eq("biz_type", bizType);
    289. }
    290. List list = fileService.findListByQueryWrapper(queryWrapper);
    291. return getLayerDataTable(list);
    292. }
    293. /**
    294. * 将 *,*,*,*,*, 样的ID放入Set中
    295. * @return 包含对应各id的数值的列表, 不包含重复id
    296. */
    297. private static final Set splitIds(String ids){
    298. Set idsSet = new HashSet();
    299. if(ids != null && !ids.isEmpty()){
    300. String[] data = ids.split(",");
    301. if(data != null){
    302. for(String d : data){
    303. idsSet.add(d);
    304. }
    305. }
    306. }
    307. return idsSet;
    308. }
    309. /**
    310. * 下载文件
    311. * @param fid
    312. * @param response
    313. */
    314. @RequestMapping("/download")
    315. public void download(String fid, HttpServletResponse response) throws Exception{
    316. FileEntity file = fileService.getOneByFid(fid);
    317. if (file == null) {
    318. return;
    319. }
    320. OutputStream to = null;
    321. try {
    322. String filename = file.getName();
    323. response.setContentType("text/html");
    324. response.setHeader("Content-Disposition","attachment;filename=\"" + new String(filename.getBytes(), "ISO-8859-1") + "\"");
    325. to = response.getOutputStream();
    326. this.getFileInfo(to, file);
    327. } catch (Exception e) {
    328. e.printStackTrace();
    329. } finally {
    330. if (to != null) {
    331. try {
    332. to.flush();
    333. to.close();
    334. } catch (IOException e) {
    335. }
    336. }
    337. }
    338. }
    339. private void getFileInfo(OutputStream to,FileEntity file) throws Exception{
    340. InputStream in = null;
    341. try{
    342. File file_download = new File(HngtghyConfig.getProfile() + "/" + file.getPath());
    343. in = new FileInputStream(file_download);
    344. byte[] buffer = new byte[1024];
    345. int got = -1;
    346. while ((got = in.read(buffer)) != -1){
    347. to.write(buffer,0,got);
    348. }
    349. }catch(Exception e){
    350. throw e;
    351. }finally{
    352. if(in != null){
    353. try {
    354. in.close();
    355. } catch (IOException e) {
    356. }
    357. }
    358. }
    359. }
    360. }

    其类图如下所示:

     

            以上就是完整的后端接收处理逻辑,使用java代码进行开发,这里使用了本地磁盘的方式进行存储,你可以自己扩展一下,比如可以集成分布式存储,都是可以的,这样改造后可以当成企业统一的服务。

     三、总结

            以上就是本文的主要内容,介绍统一附件管理服务的后台开发逻辑。简单介绍了后台附件表的设计,以后围绕附件管理的相关实体定义、服务层、控制层定义。通过给出相关类的类图,便于大家对整体的代码层次有一个比较全面的认识。

  • 相关阅读:
    web前端 html+css+javascript游戏网页设计实例 (网页制作课作业)
    docker 安装 redis
    Echarts社区开源地址
    JavaScript
    xray证书安装使用及Burp联动
    添砖Java之路(其六)——通过集合制作的学生信息管理系统
    C++学习之强制类型转换
    vue 的 render 函数的用法:new Vue({ render: h => h(App), }).$mount(‘#app‘)
    Java筑基32-IO流02-节点流&处理流
    「笔翰如流」上线,TFS-CLUB 社区活动来袭 〖 你更文、我送礼 〗限量绝版谷歌周边等你拿
  • 原文地址:https://blog.csdn.net/yelangkingwuzuhu/article/details/127361371