第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全網最全的資料