第2-3-7章 个人网盘服务接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss


目录
  • 第2-1-2章 传统方式安装FastDFS-附FastDFS常用命令
    第2-1-3章 docker-compose安装FastDFS,实现文件存储服务
    第2-1-5章 docker安装MinIO实现文件存储服务-springboot整合minio-minio全网最全的资料

    全套代码及资料全部完整提供,点此处下载

    5.8.1 接口导入-分页查询附件

    接口文档:

    在这里插入图片描述

    AttachmentController代码:

    /**
    * 分页查询附件
    *
    */
    @ApiOperation(value = "分页查询附件", notes = "分页查询附件")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "current", value = "当前页", dataType = "long", paramType = "query", defaultValue = "1"),
        @ApiImplicitParam(name = "size", value = "每页显示几条", dataType = "long", paramType = "query", defaultValue = "10"),
    })
    @GetMapping(value = "/page")
    public R> page(FilePageReqDTO data) {
        Page page = getPage();
        attachmentService.page(page, data);
        return success(page);
    }
    

    AttachmentService接口:

    /**
    * 查询附件分页数据
    *
    * @param page
    * @param data
    * @return
    */
    IPage page(Page page, FilePageReqDTO data);
    

    AttachmentServiceImpl类:

    /**
    * 查询附件分页数据
    *
    * @param page
    * @param data
    * @return
    */
    public IPage page(Page page, FilePageReqDTO data) {
        Attachment attachment = dozer.map(data, Attachment.class);
    
        // ${ew.customSqlSegment} 语法一定要手动eq like 等 不能用lbQ!
        LbqWrapper wrapper = Wraps.lbQ()
            .like(Attachment::getSubmittedFileName, attachment.getSubmittedFileName())
            .like(Attachment::getBizType, attachment.getBizType())
            .like(Attachment::getBizId, attachment.getBizId())
            .eq(Attachment::getDataType, attachment.getDataType())
            .orderByDesc(Attachment::getId);
        return baseMapper.page(page, wrapper);
    }
    

    5.8.2 接口导入-根据业务类型/业务id查询附件

    接口文档:
    在这里插入图片描述

    AttachmentController代码:

    @ApiOperation(value = "查询附件", notes = "查询附件")
    @ApiResponses(
        @ApiResponse(code = 60103, message = "文件id为空")
    )
    @GetMapping
    public R> findAttachment(@RequestParam(value = "bizTypes", required = false) String[] bizTypes,
                                                       @RequestParam(value = "bizIds", required = false) String[] bizIds) {
        //不能同时为空
        BizAssert.isTrue(!(ArrayUtils.isEmpty(bizTypes) && ArrayUtils.isEmpty(bizIds)), BASE_VALID_PARAM.build("业务类型不能为空"));
        return success(attachmentService.find(bizTypes, bizIds));
    }
    

    AttachmentService接口:

    /**
    * 根据业务类型和业务id查询附件
    *
    * @param bizTypes
    * @param bizIds
    * @return
    */
    List find(String[] bizTypes, String[] bizIds);
    

    AttachmentServiceImpl类:

    /**
    * 根据业务类型和业务id查询附件
    *
    * @param bizTypes
    * @param bizIds
    * @return
    */
    public List find(String[] bizTypes, String[] bizIds) {
            return baseMapper.find(bizTypes, bizIds);
    }
    

    5.9 导入网盘服务接口

    前面我们已经完成了文件服务中的附件服务相关接口的开发,附件服务最终是将上传的文件信息保存在pd_attachment表中。

    本小节要完成的是文件服务中的网盘服务功能,此功能最终是将上传的文件信息保存在pd_file表中。网盘服务和附件服务非常类似,只是多了一个文件夹的概念。

    5.9.1 导入FileController

    package com.itheima.pinda.file.controller;
    
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.itheima.pinda.base.BaseController;
    import com.itheima.pinda.base.R;
    import com.itheima.pinda.dozer.DozerUtils;
    import com.itheima.pinda.file.dto.FilePageReqDTO;
    import com.itheima.pinda.file.dto.FileUpdateDTO;
    import com.itheima.pinda.file.dto.FolderDTO;
    import com.itheima.pinda.file.dto.FolderSaveDTO;
    import com.itheima.pinda.file.entity.File;
    import com.itheima.pinda.file.manager.FileRestManager;
    import com.itheima.pinda.file.service.FileService;
    import com.itheima.pinda.log.annotation.SysLog;
    import io.swagger.annotations.*;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.validation.Valid;
    import javax.validation.constraints.NotNull;
    
    /**
     * 文件前端控制器
     */
    @Validated
    @RestController
    @RequestMapping("/file")
    @Slf4j
    @Api(value = "文件表", tags = "文件表")
    public class FileController extends BaseController {
        @Autowired
        private FileService fileService;
        @Autowired
        private FileRestManager fileRestManager;
        @Autowired
        private DozerUtils dozerUtils;
    
        /**
         * 查询单个文件信息
         *
         * @param id
         * @return
         */
        @ApiOperation(value = "查询文件", notes = "查询文件")
        @GetMapping
        public R get(@RequestParam(value = "id") Long id) {
            File file = fileService.getById(id);
            if (file != null && file.getIsDelete()) {
                return success(null);
            }
            return success(file);
        }
    
        /**
         * 获取文件分页
         */
        @ApiOperation(value = "分页查询文件", notes = "获取文件分页")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "current", value = "当前页", dataType = "long", paramType = "query", defaultValue = "1"),
                @ApiImplicitParam(name = "size", value = "每页显示几条", dataType = "long", paramType = "query", defaultValue = "10"),
        })
        @GetMapping(value = "/page")
        public R> page(FilePageReqDTO data) {
            return success(fileRestManager.page(getPage(), data));
        }
    
        /**
         * 上传文件
         */
        @ApiOperation(value = "上传文件", notes = "上传文件 ")
        @ApiResponses({
                @ApiResponse(code = 60102, message = "文件夹为空"),
        })
        @ApiImplicitParams({
                @ApiImplicitParam(name = "source", value = "文件来源", dataType = "String", paramType = "query"),
                @ApiImplicitParam(name = "userId", value = "用户id", dataType = "long", paramType = "query"),
                @ApiImplicitParam(name = "folderId", value = "文件夹id", dataType = "long", paramType = "query"),
                @ApiImplicitParam(name = "file", value = "附件", dataType = "MultipartFile", allowMultiple = true, required = true),
        })
        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        public R upload(
                @NotNull(message = "文件夹不能为空")
                @RequestParam(name = "source", defaultValue = "inner") String source,
                @RequestParam(name = "userId", required = false) Long userId,
                @RequestParam(value = "folderId") Long folderId,
                @RequestParam(value = "file") MultipartFile simpleFile) {
            //1,先将文件存在本地,并且生成文件名
            log.info("contentType={}, name={} , sfname={}", simpleFile.getContentType(), simpleFile.getName(), simpleFile.getOriginalFilename());
            // 忽略路径字段,只处理文件类型
            if (simpleFile.getContentType() == null) {
                return fail("文件为空");
            }
    
            File file = fileService.upload(simpleFile, folderId, source, userId);
    
            return success(file);
        }
    
    
        /**
         * 保存文件夹
         */
        @ApiResponses({
                @ApiResponse(code = 60000, message = "文件夹为空"),
                @ApiResponse(code = 60001, message = "文件夹名称为空"),
                @ApiResponse(code = 60002, message = "父文件夹为空"),
        })
        @ApiOperation(value = "新增文件夹", notes = "新增文件夹")
        @RequestMapping(value = "", method = RequestMethod.POST)
        public R saveFolder(@Valid @RequestBody FolderSaveDTO folderSaveDto) {
            //2,获取身份
    
            FolderDTO folder = fileService.saveFolder(folderSaveDto);
            return success(folder);
        }
    
        /**
         * 修改文件、文件夹信息
         *
         * @param fileUpdateDTO
         * @return
         */
        @ApiOperation(value = "修改文件/文件夹名称", notes = "修改文件/文件夹名称")
        @ApiResponses({
                @ApiResponse(code = 60100, message = "文件为空"),
        })
        @RequestMapping(value = "", method = RequestMethod.PUT)
        public R update(@Valid @RequestBody FileUpdateDTO fileUpdateDTO) {
            // 判断文件名是否有 后缀
            if (StringUtils.isNotEmpty(fileUpdateDTO.getSubmittedFileName())) {
                File oldFile = fileService.getById(fileUpdateDTO.getId());
                if (oldFile.getExt() != null && !fileUpdateDTO.getSubmittedFileName().endsWith(oldFile.getExt())) {
                    fileUpdateDTO.setSubmittedFileName(fileUpdateDTO.getSubmittedFileName() + "." + oldFile.getExt());
                }
            }
            File file = dozerUtils.map2(fileUpdateDTO, File.class);
    
            fileService.updateById(file);
            return success(true);
        }
    
        /**
         * 根据Ids进行文件删除
         *
         * @param ids
         * @return
         */
        @ApiOperation(value = "根据Ids进行文件删除", notes = "根据Ids进行文件删除  ")
        @DeleteMapping(value = "/ids")
        public R removeList(@RequestParam(value = "ids[]") Long[] ids) {
            Long userId = getUserId();
            return success(fileService.removeList(userId, ids));
        }
    
        /**
         * 下载一个文件或多个文件打包下载
         *
         * @param ids
         * @param response
         * @throws Exception
         */
        @ApiOperation(value = "下载一个文件或多个文件打包下载", notes = "下载一个文件或多个文件打包下载")
        @GetMapping(value = "/download", produces = "application/octet-stream")
        public void download(
                @ApiParam(name = "ids[]", value = "文件id 数组")
                @RequestParam(value = "ids[]") Long[] ids,
                HttpServletRequest request, HttpServletResponse response) throws Exception {
            fileRestManager.download(request, response, ids, null);
        }
    
    }
    

    5.9.2 导入StatisticsController

    package com.itheima.pinda.file.controller;
    
    import com.itheima.pinda.base.BaseController;
    import com.itheima.pinda.base.R;
    import com.itheima.pinda.file.domain.FileStatisticsDO;
    import com.itheima.pinda.file.dto.FileOverviewDTO;
    import com.itheima.pinda.file.dto.FileStatisticsAllDTO;
    import com.itheima.pinda.file.service.FileService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import java.time.LocalDateTime;
    import java.util.List;
    
    /**
     * 文件统计接口
     */
    @Slf4j
    @RestController
    @RequestMapping("/statistics")
    @Api(value = "Statistics", tags = "统计接口")
    public class StatisticsController extends BaseController {
        @Autowired
        private FileService fileService;
    
        @ApiOperation(value = "云盘首页数据概览", notes = "云盘首页数据概览")
        @GetMapping(value = "/overview")
        public R overview(@RequestParam(name = "userId", required = false) Long userId) {
            return success(fileService.findOverview(userId, null, null));
        }
    
        @ApiOperation(value = "按照类型,统计各种类型的 大小和数量", notes = "按照类型,统计当前登录人各种类型的大小和数量")
        @GetMapping(value = "/type")
        public R> findAllByDataType(@RequestParam(name = "userId", required = false) Long userId) {
            return success(fileService.findAllByDataType(userId));
        }
    
        @ApiOperation(value = "按照时间统计各种类型的文件的数量和大小", notes = "按照时间统计各种类型的文件的数量和大小 不指定时间,默认查询一个月")
        @GetMapping(value = "")
        public R findNumAndSizeToTypeByDate(@RequestParam(name = "userId", required = false) Long userId,
                                                                  @RequestParam(value = "startTime", required = false) LocalDateTime startTime,
                                                                  @RequestParam(value = "endTime", required = false) LocalDateTime endTime) {
            return success(fileService.findNumAndSizeToTypeByDate(userId, startTime, endTime));
        }
    }
    

    5.9.3 导入FileRestManager

    package com.itheima.pinda.file.manager;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.itheima.pinda.context.BaseContextHandler;
    import com.itheima.pinda.file.constant.FileConstants;
    import com.itheima.pinda.file.dto.FilePageReqDTO;
    import com.itheima.pinda.file.entity.File;
    import com.itheima.pinda.file.service.FileService;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import static com.itheima.pinda.utils.StrPool.DEF_PARENT_ID;
    
    /**
     * 文件 公共代码 管理类
     */
    @Component
    public class FileRestManager {
        @Autowired
        private FileService fileService;
    
        public IPage page(IPage page, FilePageReqDTO filePageReq) {
            //类型和文件夹id同时为null时, 表示查询 全部文件
            if (filePageReq.getFolderId() == null && filePageReq.getDataType() == null) {
                filePageReq.setFolderId(DEF_PARENT_ID);
            }
    
            QueryWrapper query = new QueryWrapper<>();
            LambdaQueryWrapper lambdaQuery = query.lambda()
                    .eq(File::getIsDelete, false)
                    .eq(filePageReq.getDataType() != null, File::getDataType, filePageReq.getDataType())
                    .eq(filePageReq.getFolderId() != null, File::getFolderId, filePageReq.getFolderId())
                    .like(StringUtils.isNotEmpty(filePageReq.getSubmittedFileName()), File::getSubmittedFileName, filePageReq.getSubmittedFileName());
    
            query.orderByDesc(String.format("case when %s='DIR' THEN 1 else 0 end", FileConstants.DATA_TYPE));
            lambdaQuery.orderByDesc(File::getCreateTime);
    
            fileService.page(page, lambdaQuery);
            return page;
        }
    
        public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids, Long userId) throws Exception {
            userId = userId == null || userId <= 0 ? BaseContextHandler.getUserId() : userId;
            fileService.download(request, response, ids, userId);
        }
    }
    

    5.9.4 导入FileService

    package com.itheima.pinda.file.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.itheima.pinda.file.domain.FileAttrDO;
    import com.itheima.pinda.file.domain.FileStatisticsDO;
    import com.itheima.pinda.file.dto.FileOverviewDTO;
    import com.itheima.pinda.file.dto.FileStatisticsAllDTO;
    import com.itheima.pinda.file.dto.FolderDTO;
    import com.itheima.pinda.file.dto.FolderSaveDTO;
    import com.itheima.pinda.file.entity.File;
    import org.springframework.web.multipart.MultipartFile;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.time.LocalDateTime;
    import java.util.List;
    /**
     * 业务接口
     * 文件表
     *
     */
    public interface FileService extends IService {
        /**
         * 保存文件夹
         *
         * @param folderSaveDto 文件夹
         * @return
         */
        FolderDTO saveFolder(FolderSaveDTO folderSaveDto);
    
        /**
         * 根据文件id下载文件,并统计下载次数
         *
         * @param request  请求
         * @param response 响应
         * @param ids      文件id集合
         * @param userId   用户id
         * @throws Exception
         */
        void download(HttpServletRequest request, HttpServletResponse response,
                      Long[] ids, Long userId) throws Exception;
    
        /**
         * 根据文件id和用户id 删除文件或者文件夹
         *
         * @param userId 用户id
         * @param ids    文件id集合
         * @return
         */
        Boolean removeList(Long userId, Long[] ids);
    
        /**
         * 根据文件夹id查询
         *
         * @param folderId
         * @return
         */
        FileAttrDO getFileAttrDo(Long folderId);
    
        /**
         * 文件上传
         *
         * @param simpleFile 文件
         * @param folderId   文件夹id
         * @return
         */
        File upload(MultipartFile simpleFile, Long folderId);
    
    	/**
    	 * 文件上传
    	 *
    	 * @param simpleFile
    	 * @param folderId
    	 * @param source
    	 * @param userId
    	 * @return
    	 */
    	File upload(MultipartFile simpleFile, Long folderId, String source, Long userId);
    
        /**
         * 首页概览
         *
         * @param userId
         * @param startTime
         * @param endTime
         * @return
         */
        FileOverviewDTO findOverview(Long userId, LocalDateTime startTime, LocalDateTime endTime);
    
        /**
         * 首页个人文件发展概览
         *
         * @param userId
         * @param startTime
         * @param endTime
         * @return
         */
        FileStatisticsAllDTO findAllByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime);
    
    
        /**
         * 按照 数据类型分类查询 当前人的所有文件的数量和大小
         *
         * @param userId
         * @return
         */
        List findAllByDataType(Long userId);
    
        /**
         * 查询下载排行前20的文件
         *
         * @param userId
         * @return
         */
        List downTop20(Long userId);
    
        /**
         * 根据日期查询,特定类型的数量和大小
         *
         * @param userId
         * @param startTime
         * @param endTime
         * @return
         */
        FileStatisticsAllDTO findNumAndSizeToTypeByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime);
    
        /**
         * 根据日期查询下载大小
         *
         * @param userId
         * @param startTime
         * @param endTime
         * @return
         */
        FileStatisticsAllDTO findDownSizeByDate(Long userId, LocalDateTime startTime,
                                                LocalDateTime endTime);
    }
    

    5.9.5 导入FileServiceImpl

    package com.itheima.pinda.file.service.impl;
    
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.itheima.pinda.database.mybatis.conditions.Wraps;
    import com.itheima.pinda.database.mybatis.conditions.update.LbuWrapper;
    import com.itheima.pinda.dozer.DozerUtils;
    import com.itheima.pinda.file.biz.FileBiz;
    import com.itheima.pinda.file.dao.FileMapper;
    import com.itheima.pinda.file.domain.FileAttrDO;
    import com.itheima.pinda.file.domain.FileDO;
    import com.itheima.pinda.file.domain.FileDeleteDO;
    import com.itheima.pinda.file.domain.FileStatisticsDO;
    import com.itheima.pinda.file.dto.FileOverviewDTO;
    import com.itheima.pinda.file.dto.FileStatisticsAllDTO;
    import com.itheima.pinda.file.dto.FolderDTO;
    import com.itheima.pinda.file.dto.FolderSaveDTO;
    import com.itheima.pinda.file.entity.File;
    import com.itheima.pinda.file.enumeration.DataType;
    import com.itheima.pinda.file.enumeration.IconType;
    import com.itheima.pinda.file.service.FileService;
    import com.itheima.pinda.file.strategy.FileStrategy;
    import com.itheima.pinda.utils.BizAssert;
    import com.itheima.pinda.utils.DateUtils;
    import lombok.Getter;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.ArrayUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    import static com.itheima.pinda.exception.code.ExceptionCode.BASE_VALID_PARAM;
    import static com.itheima.pinda.utils.StrPool.DEF_PARENT_ID;
    import static com.itheima.pinda.utils.StrPool.DEF_ROOT_PATH;
    import static java.util.stream.Collectors.groupingBy;
    
    /**
     * 业务实现类
     * 文件表
     */
    @Slf4j
    @Service
    public class FileServiceImpl extends ServiceImpl implements FileService {
        @Autowired
        private DozerUtils dozerUtils;
    
        @Autowired
        private FileBiz fileBiz;
        @Resource
        private FileStrategy fileStrategy;
    
        @Override
        public File upload(MultipartFile simpleFile, Long folderId) {
            FileAttrDO fileAttrDO = this.getFileAttrDo(folderId);
            String treePath = fileAttrDO.getTreePath();
            String folderName = fileAttrDO.getFolderName();
            Integer grade = fileAttrDO.getGrade();
    
            File file = fileStrategy.upload(simpleFile);
            file.setFolderId(folderId);
            file.setFolderName(folderName);
            file.setGrade(grade);
            file.setTreePath(treePath);
            super.save(file);
            return file;
        }
    
    	@Override
    	public File upload(MultipartFile simpleFile, Long folderId, String source, Long userId) {
    		FileAttrDO fileAttrDO = this.getFileAttrDo(folderId);
    		String treePath = fileAttrDO.getTreePath();
    		String folderName = fileAttrDO.getFolderName();
    		Integer grade = fileAttrDO.getGrade();
    
    		File file = fileStrategy.upload(simpleFile);
    		file.setFolderId(folderId);
    		file.setFolderName(folderName);
    		file.setGrade(grade);
    		file.setTreePath(treePath);
    
    		file.setSource(source);
    		if (userId != null) {
    			file.setCreateUser(userId);
    		}
    
    		super.save(file);
    		return file;
    	}
    
    	@Override
        public FileAttrDO getFileAttrDo(Long folderId) {
            String treePath = DEF_ROOT_PATH;
            String folderName = "";
            Integer grade = 1;
            if (folderId == null || folderId <= 0) {
                return new FileAttrDO(treePath, grade, folderName, DEF_PARENT_ID);
            }
            File folder = this.getById(folderId);
    
            if (folder != null && !folder.getIsDelete() && DataType.DIR.eq(folder.getDataType())) {
                folderName = folder.getSubmittedFileName();
                treePath = StringUtils.join(folder.getTreePath(), folder.getId(), DEF_ROOT_PATH);
                grade = folder.getGrade() + 1;
            }
            BizAssert.isTrue(grade <= 10, BASE_VALID_PARAM.build("文件夹层级不能超过10层"));
            return new FileAttrDO(treePath, grade, folderName, folderId);
        }
    
    
        @Override
        public FolderDTO saveFolder(FolderSaveDTO folderSaveDto) {
            File folder = dozerUtils.map2(folderSaveDto, File.class);
            if (folderSaveDto.getFolderId() == null || folderSaveDto.getFolderId() <= 0) {
                folder.setFolderId(DEF_PARENT_ID);
                folder.setTreePath(DEF_ROOT_PATH);
                folder.setGrade(1);
            } else {
                File parent = super.getById(folderSaveDto.getFolderId());
                BizAssert.notNull(parent, BASE_VALID_PARAM.build("父文件夹不能为空"));
                BizAssert.isFalse(parent.getIsDelete(), BASE_VALID_PARAM.build("父文件夹已经被删除"));
                BizAssert.equals(DataType.DIR.name(), parent.getDataType().name(), BASE_VALID_PARAM.build("父文件夹不存在"));
                BizAssert.isTrue(parent.getGrade() < 10, BASE_VALID_PARAM.build("文件夹层级不能超过10层"));
                folder.setFolderName(parent.getSubmittedFileName());
                folder.setTreePath(StringUtils.join(parent.getTreePath(), parent.getId(), DEF_ROOT_PATH));
                folder.setGrade(parent.getGrade() + 1);
            }
            if (folderSaveDto.getOrderNum() == null) {
                folderSaveDto.setOrderNum(0);
            }
            folder.setIsDelete(false);
            folder.setDataType(DataType.DIR);
            folder.setIcon(IconType.DIR.getIcon());
            setDate(folder);
            super.save(folder);
            return dozerUtils.map2(folder, FolderDTO.class);
        }
    
        private void setDate(File file) {
            LocalDateTime now = LocalDateTime.now();
            file.setCreateMonth(DateUtils.formatAsYearMonthEn(now))
                    .setCreateWeek(DateUtils.formatAsYearWeekEn(now))
                    .setCreateDay(DateUtils.formatAsDateEn(now));
        }
    
        public boolean removeFile(Long[] ids, Long userId) {
            LbuWrapper lambdaUpdate =
                    Wraps.lbU()
                            .in(File::getId, ids)
                            .eq(File::getCreateUser, userId);
            File file = File.builder().isDelete(Boolean.TRUE).build();
    
            return super.update(file, lambdaUpdate);
        }
    
        @Override
        public Boolean removeList(Long userId, Long[] ids) {
            if (ArrayUtils.isEmpty(ids)) {
                return Boolean.TRUE;
            }
            List list = super.list(Wrappers.lambdaQuery().in(File::getId, ids));
            if (list.isEmpty()) {
                return true;
            }
            super.removeByIds(Arrays.asList(ids));
    
            fileStrategy.delete(list.stream().map((fi) -> FileDeleteDO.builder()
                    .relativePath(fi.getRelativePath())
                    .fileName(fi.getFilename())
                    .group(fi.getGroup())
                    .path(fi.getPath())
                    .file(false)
                    .build())
                    .collect(Collectors.toList()));
            return true;
        }
    
        @Override
        public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids, Long userId) throws Exception {
            if (ids == null || ids.length == 0) {
                return;
            }
            List list = (List) super.listByIds(Arrays.asList(ids));
    
            if (list == null || list.size() == 0) {
                return;
            }
            List listDo = list.stream().map((file) ->
                    FileDO.builder()
                            .dataType(file.getDataType())
                            .size(file.getSize())
                            .submittedFileName(file.getSubmittedFileName())
                            .url(file.getUrl())
                            .build())
                    .collect(Collectors.toList());
            fileBiz.down(listDo, request, response);
        }
    
    
        @Override
        public FileOverviewDTO findOverview(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
            InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
            startTime = innerQueryDate.getStartTime();
            endTime = innerQueryDate.getEndTime();
    
            List list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", startTime, endTime);
            FileOverviewDTO.FileOverviewDTOBuilder builder = FileOverviewDTO.myBuilder();
    
            long allSize = 0L;
            int allNum = 0;
            for (FileStatisticsDO fs : list) {
                allSize += fs.getSize();
                allNum += fs.getNum();
                switch (fs.getDataType()) {
                    case DIR:
                        builder.dirNum(fs.getNum());
                        break;
                    case IMAGE:
                        builder.imgNum(fs.getNum());
                        break;
                    case VIDEO:
                        builder.videoNum(fs.getNum());
                        break;
                    case DOC:
                        builder.docNum(fs.getNum());
                        break;
                    case AUDIO:
                        builder.audioNum(fs.getNum());
                        break;
                    case OTHER:
                        builder.otherNum(fs.getNum());
                        break;
                    default:
                        break;
                }
            }
            builder.allFileNum(allNum).allFileSize(allSize);
            return builder.build();
        }
    
        @Override
        public FileStatisticsAllDTO findAllByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
            InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
            startTime = innerQueryDate.getStartTime();
            endTime = innerQueryDate.getEndTime();
            List dateList = innerQueryDate.getDateList();
            String dateType = innerQueryDate.getDateType();
    
            //不完整的数据
            List list = baseMapper.findNumAndSizeByUserId(userId, dateType, null, startTime, endTime);
    
            //按月份分类
            Map> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType));
    
            List sizeList = new ArrayList<>();
            List numList = new ArrayList<>();
    
            dateList.forEach((date) -> {
                if (map.containsKey(date)) {
                    List subList = map.get(date);
    
                    Long size = subList.stream().mapToLong(FileStatisticsDO::getSize).sum();
                    Integer num = subList.stream().filter((fs) -> !DataType.DIR.eq(fs.getDataType()))
                            .mapToInt(FileStatisticsDO::getNum).sum();
                    sizeList.add(size);
                    numList.add(num);
                } else {
                    sizeList.add(0L);
                    numList.add(0);
                }
            });
    
            return FileStatisticsAllDTO.builder().dateList(dateList).numList(numList).sizeList(sizeList).build();
        }
    
    
        @Override
        public List findAllByDataType(Long userId) {
            List dataTypes = Arrays.asList(DataType.values());
            List list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", null, null);
    
            Map> map = list.stream().collect(groupingBy(FileStatisticsDO::getDataType));
    
            return dataTypes.stream().map((type) -> {
                FileStatisticsDO fs = null;
                if (map.containsKey(type)) {
                    fs = map.get(type).get(0);
                } else {
                    fs = FileStatisticsDO.builder().dataType(type).size(0L).num(0).build();
                }
                return fs;
            }).collect(Collectors.toList());
        }
    
        @Override
        public List downTop20(Long userId) {
            return baseMapper.findDownTop20(userId);
        }
    
        @Override
        public FileStatisticsAllDTO findNumAndSizeToTypeByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
            return common(userId, startTime, endTime,
                    (qd) -> baseMapper.findNumAndSizeByUserId(qd.getUserId(), qd.getDateType(), "ALL", qd.getStartTime(), qd.getEndTime()));
        }
    
        @Override
        public FileStatisticsAllDTO findDownSizeByDate(Long userId, LocalDateTime startTime,
                                                       LocalDateTime endTime) {
            return common(userId, startTime, endTime,
                    (qd) -> baseMapper.findDownSizeByDate(qd.getUserId(), qd.getDateType(), qd.getStartTime(), qd.getEndTime()));
        }
    
        /**
         * 抽取公共查询公共代码
         *
         * @param userId    用户id
         * @param startTime 开始时间
         * @param endTime   结束时间
         * @param function  回调函数
         * @return
         */
        private FileStatisticsAllDTO common(Long userId, LocalDateTime startTime, LocalDateTime endTime, Function> function) {
            InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
            List dateList = innerQueryDate.getDateList();
    
            List list = function.apply(innerQueryDate);
    
            //按月份分类
            Map> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType));
    
            List sizeList = new ArrayList<>(dateList.size());
            List numList = new ArrayList<>(dateList.size());
    
            List dirNumList = new ArrayList<>(dateList.size());
    
            List imgSizeList = new ArrayList<>(dateList.size());
            List imgNumList = new ArrayList<>(dateList.size());
    
            List videoSizeList = new ArrayList<>(dateList.size());
            List videoNumList = new ArrayList<>(dateList.size());
    
            List audioSizeList = new ArrayList<>(dateList.size());
            List audioNumList = new ArrayList<>(dateList.size());
    
            List docSizeList = new ArrayList<>(dateList.size());
            List docNumList = new ArrayList<>(dateList.size());
    
            List otherSizeList = new ArrayList<>(dateList.size());
            List otherNumList = new ArrayList<>(dateList.size());
    
            dateList.forEach((date) -> {
                if (map.containsKey(date)) {
                    List subList = map.get(date);
    
                    Function> stream = (dataType) -> subList.stream().filter((fs) -> !dataType.eq(fs.getDataType()));
                    Long size = stream.apply(DataType.DIR).mapToLong(FileStatisticsDO::getSize).sum();
                    Integer num = stream.apply(DataType.DIR).mapToInt(FileStatisticsDO::getNum).sum();
                    sizeList.add(size);
                    numList.add(num);
    
                    Integer dirNum = subList.stream().filter((fs) -> DataType.DIR.eq(fs.getDataType()))
                            .mapToInt(FileStatisticsDO::getNum).sum();
                    dirNumList.add(dirNum);
    
                    add(imgSizeList, imgNumList, subList, DataType.IMAGE);
                    add(videoSizeList, videoNumList, subList, DataType.VIDEO);
                    add(audioSizeList, audioNumList, subList, DataType.AUDIO);
                    add(docSizeList, docNumList, subList, DataType.DOC);
                    add(otherSizeList, otherNumList, subList, DataType.OTHER);
    
                } else {
                    sizeList.add(0L);
                    numList.add(0);
                    dirNumList.add(0);
                    imgSizeList.add(0L);
                    imgNumList.add(0);
                    videoSizeList.add(0L);
                    videoNumList.add(0);
                    audioSizeList.add(0L);
                    audioNumList.add(0);
                    docSizeList.add(0L);
                    docNumList.add(0);
                    otherSizeList.add(0L);
                    otherNumList.add(0);
                }
            });
    
            return FileStatisticsAllDTO.builder()
                    .dateList(dateList)
                    .numList(numList).sizeList(sizeList)
                    .dirNumList(dirNumList)
                    .imgNumList(imgNumList).imgSizeList(imgSizeList)
                    .videoNumList(videoNumList).videoSizeList(videoSizeList)
                    .audioNumList(audioNumList).audioSizeList(audioSizeList)
                    .docNumList(docNumList).docSizeList(docSizeList)
                    .otherNumList(otherNumList).otherSizeList(otherSizeList)
                    .build();
        }
    
        private void add(List sizeList, List numList, List subList, DataType dt) {
            Function> stream =
                    dataType -> subList.stream().filter(fs -> dataType.eq(fs.getDataType()));
    
            Long size = stream.apply(dt).mapToLong(FileStatisticsDO::getSize).sum();
            Integer num = stream.apply(dt).mapToInt(FileStatisticsDO::getNum).sum();
            sizeList.add(size);
            numList.add(num);
        }
    
        @Getter
        private static class InnerQueryDate {
            private LocalDateTime startTime;
            private LocalDateTime endTime;
            private List dateList;
            private String dateType;
            private Long userId;
    
            public InnerQueryDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
                this.userId = userId;
                this.startTime = startTime;
                this.endTime = endTime;
            }
    
            public InnerQueryDate invoke() {
                if (startTime == null) {
                    startTime = LocalDateTime.now().plusDays(-9);
                }
                if (endTime == null) {
                    endTime = LocalDateTime.now();
                }
                endTime = LocalDateTime.of(endTime.toLocalDate(), LocalTime.MAX);
                dateList = new ArrayList<>();
                dateType = DateUtils.calculationEn(startTime, endTime, dateList);
                return this;
            }
        }
    }
    

    5.9.6 扩展FileMapper接口方法

    /**
    * 查询下次次数前20的文件
    *
    * @param userId
    * @return
    */
    List findDownTop20(@Param("userId") Long userId);
    
    /**
    * 统计时间区间内文件的下次次数和大小
    *
    * @param userId
    * @param dateType  日期类型 {MONTH:按月;WEEK:按周;DAY:按日} 来统计
    * @param startTime
    * @param endTime
    * @return
    */
    List findDownSizeByDate(@Param("userId") Long userId,
                                              @Param("dateType") String dateType,
                                              @Param("startTime") LocalDateTime startTime,
                                              @Param("endTime") LocalDateTime endTime);
    

    第2-1-2章 传统方式安装FastDFS-附FastDFS常用命令
    第2-1-3章 docker-compose安装FastDFS,实现文件存储服务
    第2-1-5章 docker安装MinIO实现文件存储服务-springboot整合minio-minio全网最全的资料

    全套代码及资料全部完整提供,点此处下载