第2-3-7章 个人网盘服务接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
- 2022 年 11 月 20 日
- 筆記
- FastDFS, Java开发, minio, nacos, Nginx, OSS, springboot, 中台, 中台战略与组件化开发, 文件存储服务, 网盘
5.8 导入其他接口代码
第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<IPage<Attachment>> page(FilePageReqDTO data) {
Page<Attachment> page = getPage();
attachmentService.page(page, data);
return success(page);
}
AttachmentService接口:
/**
* 查询附件分页数据
*
* @param page
* @param data
* @return
*/
IPage<Attachment> page(Page<Attachment> page, FilePageReqDTO data);
AttachmentServiceImpl类:
/**
* 查询附件分页数据
*
* @param page
* @param data
* @return
*/
public IPage<Attachment> page(Page<Attachment> page, FilePageReqDTO data) {
Attachment attachment = dozer.map(data, Attachment.class);
// ${ew.customSqlSegment} 语法一定要手动eq like 等 不能用lbQ!
LbqWrapper<Attachment> wrapper = Wraps.<Attachment>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<List<AttachmentResultDTO>> 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<AttachmentResultDTO> find(String[] bizTypes, String[] bizIds);
AttachmentServiceImpl类:
/**
* 根据业务类型和业务id查询附件
*
* @param bizTypes
* @param bizIds
* @return
*/
public List<AttachmentResultDTO> 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<File> 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<IPage<File>> 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<File> 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<FolderDTO> 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<Boolean> 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<Boolean> 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<FileOverviewDTO> overview(@RequestParam(name = "userId", required = false) Long userId) {
return success(fileService.findOverview(userId, null, null));
}
@ApiOperation(value = "按照类型,统计各种类型的 大小和数量", notes = "按照类型,统计当前登录人各种类型的大小和数量")
@GetMapping(value = "/type")
public R<List<FileStatisticsDO>> findAllByDataType(@RequestParam(name = "userId", required = false) Long userId) {
return success(fileService.findAllByDataType(userId));
}
@ApiOperation(value = "按照时间统计各种类型的文件的数量和大小", notes = "按照时间统计各种类型的文件的数量和大小 不指定时间,默认查询一个月")
@GetMapping(value = "")
public R<FileStatisticsAllDTO> 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<File> page(IPage<File> page, FilePageReqDTO filePageReq) {
//类型和文件夹id同时为null时, 表示查询 全部文件
if (filePageReq.getFolderId() == null && filePageReq.getDataType() == null) {
filePageReq.setFolderId(DEF_PARENT_ID);
}
QueryWrapper<File> query = new QueryWrapper<>();
LambdaQueryWrapper<File> 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<File> {
/**
* 保存文件夹
*
* @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<FileStatisticsDO> findAllByDataType(Long userId);
/**
* 查询下载排行前20的文件
*
* @param userId
* @return
*/
List<FileStatisticsDO> 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<FileMapper, File> 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<File> lambdaUpdate =
Wraps.<File>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<File> list = super.list(Wrappers.<File>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<File> list = (List<File>) super.listByIds(Arrays.asList(ids));
if (list == null || list.size() == 0) {
return;
}
List<FileDO> 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<FileStatisticsDO> 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<String> dateList = innerQueryDate.getDateList();
String dateType = innerQueryDate.getDateType();
//不完整的数据
List<FileStatisticsDO> list = baseMapper.findNumAndSizeByUserId(userId, dateType, null, startTime, endTime);
//按月份分类
Map<String, List<FileStatisticsDO>> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType));
List<Long> sizeList = new ArrayList<>();
List<Integer> numList = new ArrayList<>();
dateList.forEach((date) -> {
if (map.containsKey(date)) {
List<FileStatisticsDO> 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<FileStatisticsDO> findAllByDataType(Long userId) {
List<DataType> dataTypes = Arrays.asList(DataType.values());
List<FileStatisticsDO> list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", null, null);
Map<DataType, List<FileStatisticsDO>> 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<FileStatisticsDO> 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<InnerQueryDate, List<FileStatisticsDO>> function) {
InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
List<String> dateList = innerQueryDate.getDateList();
List<FileStatisticsDO> list = function.apply(innerQueryDate);
//按月份分类
Map<String, List<FileStatisticsDO>> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType));
List<Long> sizeList = new ArrayList<>(dateList.size());
List<Integer> numList = new ArrayList<>(dateList.size());
List<Integer> dirNumList = new ArrayList<>(dateList.size());
List<Long> imgSizeList = new ArrayList<>(dateList.size());
List<Integer> imgNumList = new ArrayList<>(dateList.size());
List<Long> videoSizeList = new ArrayList<>(dateList.size());
List<Integer> videoNumList = new ArrayList<>(dateList.size());
List<Long> audioSizeList = new ArrayList<>(dateList.size());
List<Integer> audioNumList = new ArrayList<>(dateList.size());
List<Long> docSizeList = new ArrayList<>(dateList.size());
List<Integer> docNumList = new ArrayList<>(dateList.size());
List<Long> otherSizeList = new ArrayList<>(dateList.size());
List<Integer> otherNumList = new ArrayList<>(dateList.size());
dateList.forEach((date) -> {
if (map.containsKey(date)) {
List<FileStatisticsDO> subList = map.get(date);
Function<DataType, Stream<FileStatisticsDO>> 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<Long> sizeList, List<Integer> numList, List<FileStatisticsDO> subList, DataType dt) {
Function<DataType, Stream<FileStatisticsDO>> 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<String> 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<FileStatisticsDO> findDownTop20(@Param("userId") Long userId);
/**
* 统计时间区间内文件的下次次数和大小
*
* @param userId
* @param dateType 日期类型 {MONTH:按月;WEEK:按周;DAY:按日} 来统计
* @param startTime
* @param endTime
* @return
*/
List<FileStatisticsDO> 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全网最全的资料