From 2d14b47bcf497a04417b848b40bd9fb4c16f2658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sat, 14 Mar 2026 11:34:27 +0800 Subject: [PATCH] feat: implement file service and handler in manage-file module --- novalon-manage-api/manage-file/pom.xml | 4 + .../file/core/service/ISysFileService.java | 21 +++ .../core/service/impl/SysFileServiceImpl.java | 111 ++++++++++++++ .../manage/file/handler/SysFileHandler.java | 142 ++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/ISysFileService.java create mode 100644 novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/impl/SysFileServiceImpl.java create mode 100644 novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/SysFileHandler.java diff --git a/novalon-manage-api/manage-file/pom.xml b/novalon-manage-api/manage-file/pom.xml index 0081c34..51432db 100644 --- a/novalon-manage-api/manage-file/pom.xml +++ b/novalon-manage-api/manage-file/pom.xml @@ -26,6 +26,10 @@ org.springframework.boot spring-boot-starter-webflux + + com.fasterxml.jackson.core + jackson-databind + org.springframework.boot spring-boot-starter-test diff --git a/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/ISysFileService.java b/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/ISysFileService.java new file mode 100644 index 0000000..afb71f0 --- /dev/null +++ b/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/ISysFileService.java @@ -0,0 +1,21 @@ +package cn.novalon.manage.file.core.service; + +import cn.novalon.manage.file.core.domain.SysFile; +import org.springframework.http.codec.multipart.FilePart; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface ISysFileService { + + Flux getAllFiles(); + + Mono getFileById(Long id); + + Flux getFilesByUser(String username); + + Mono uploadFile(FilePart filePart, String username); + + Mono downloadFile(Long id); + + Mono deleteFile(Long id); +} diff --git a/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/impl/SysFileServiceImpl.java b/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/impl/SysFileServiceImpl.java new file mode 100644 index 0000000..65d7ac6 --- /dev/null +++ b/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/core/service/impl/SysFileServiceImpl.java @@ -0,0 +1,111 @@ +package cn.novalon.manage.file.core.service.impl; + +import cn.novalon.manage.file.core.domain.SysFile; +import cn.novalon.manage.file.core.repository.ISysFileRepository; +import cn.novalon.manage.file.core.service.ISysFileService; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +public class SysFileServiceImpl implements ISysFileService { + + private final ISysFileRepository fileRepository; + private final String uploadDir = "/app/uploads"; + + public SysFileServiceImpl(ISysFileRepository fileRepository) { + this.fileRepository = fileRepository; + } + + @Override + public Flux getAllFiles() { + return fileRepository.findByDeletedAtIsNullOrderByCreatedAtDesc(); + } + + @Override + public Mono getFileById(Long id) { + return fileRepository.findById(id); + } + + @Override + public Flux getFilesByUser(String username) { + return fileRepository.findByCreateByOrderByCreatedAtDesc(username); + } + + @Override + public Mono uploadFile(FilePart filePart, String username) { + String originalFilename = filePart.filename(); + String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".")); + String newFileName = UUID.randomUUID().toString() + fileExtension; + + Path uploadPath = Paths.get(uploadDir); + return Mono.fromCallable(() -> { + if (!Files.exists(uploadPath)) { + Files.createDirectories(uploadPath); + } + return uploadPath; + }) + .flatMap(path -> { + Path filePath = path.resolve(newFileName); + return filePart.transferTo(filePath.toFile()) + .thenReturn(filePath); + }) + .flatMap(filePath -> { + try { + long fileSize = Files.size(filePath); + String contentType = filePart.headers().getContentType() != null + ? filePart.headers().getContentType().toString() + : "application/octet-stream"; + + SysFile sysFile = new SysFile(); + sysFile.setFileName(originalFilename); + sysFile.setFilePath(filePath.toString()); + sysFile.setFileSize(String.valueOf(fileSize)); + sysFile.setFileType(contentType); + sysFile.setStorageType("LOCAL"); + sysFile.setCreateBy(username); + sysFile.setCreatedAt(LocalDateTime.now()); + + return fileRepository.save(sysFile); + } catch (IOException e) { + return Mono.error(e); + } + }); + } + + @Override + public Mono downloadFile(Long id) { + return fileRepository.findById(id) + .flatMap(file -> { + try { + Path filePath = Paths.get(file.getFilePath()); + byte[] fileContent = Files.readAllBytes(filePath); + return Mono.empty(); + } catch (IOException e) { + return Mono.error(e); + } + }); + } + + @Override + public Mono deleteFile(Long id) { + return fileRepository.findById(id) + .flatMap(file -> { + try { + Path filePath = Paths.get(file.getFilePath()); + Files.deleteIfExists(filePath); + return fileRepository.deleteByIdAndDeletedAtIsNull(id); + } catch (IOException e) { + return Mono.error(e); + } + }); + } +} diff --git a/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/SysFileHandler.java b/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/SysFileHandler.java new file mode 100644 index 0000000..9a227ed --- /dev/null +++ b/novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/SysFileHandler.java @@ -0,0 +1,142 @@ +package cn.novalon.manage.file.handler; + +import cn.novalon.manage.file.core.domain.SysFile; +import cn.novalon.manage.file.core.service.ISysFileService; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +@Component +public class SysFileHandler { + + private final ISysFileService fileService; + + public SysFileHandler(ISysFileService fileService) { + this.fileService = fileService; + } + + public Mono getAllFiles(ServerRequest request) { + Flux files = fileService.getAllFiles(); + return ServerResponse.ok().body(files, SysFile.class); + } + + public Mono getFileById(ServerRequest request) { + Long id = Long.parseLong(request.pathVariable("id")); + return fileService.getFileById(id) + .flatMap(file -> ServerResponse.ok().bodyValue(file)) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + public Mono uploadFile(ServerRequest request) { + String username = request.headers().firstHeader("X-Username"); + if (username == null) { + username = "system"; + } + final String finalUsername = username; + + return request.multipartData() + .flatMap(multipartData -> { + var part = multipartData.getFirst("file"); + if (part == null) { + return ServerResponse.badRequest().bodyValue("No file uploaded"); + } + + if (!(part instanceof FilePart)) { + return ServerResponse.badRequest().bodyValue("Invalid file part"); + } + + final FilePart filePart = (FilePart) part; + return fileService.uploadFile(filePart, finalUsername) + .flatMap(file -> ServerResponse.ok().bodyValue(file)); + }) + .switchIfEmpty(ServerResponse.badRequest().bodyValue("No file data")); + } + + public Mono downloadFile(ServerRequest request) { + Long id = Long.parseLong(request.pathVariable("id")); + return fileService.getFileById(id) + .flatMap(file -> { + try { + Path filePath = Paths.get(file.getFilePath()); + byte[] fileContent = Files.readAllBytes(filePath); + return ServerResponse.ok() + .header("Content-Disposition", "attachment; filename=\"" + file.getFileName() + "\"") + .header("Content-Type", file.getFileType()) + .bodyValue(fileContent); + } catch (Exception e) { + return ServerResponse.notFound().build(); + } + }) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + public Mono downloadFileByName(ServerRequest request) { + String fileName = request.pathVariable("fileName"); + return fileService.getAllFiles() + .filter(file -> file.getFileName().equals(fileName)) + .next() + .flatMap(file -> { + try { + Path filePath = Paths.get(file.getFilePath()); + byte[] fileContent = Files.readAllBytes(filePath); + return ServerResponse.ok() + .header("Content-Disposition", "attachment; filename=\"" + file.getFileName() + "\"") + .header("Content-Type", file.getFileType()) + .bodyValue(fileContent); + } catch (Exception e) { + return ServerResponse.notFound().build(); + } + }) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + public Mono previewFile(ServerRequest request) { + Long id = Long.parseLong(request.pathVariable("id")); + return fileService.getFileById(id) + .flatMap(file -> { + try { + Path filePath = Paths.get(file.getFilePath()); + byte[] fileContent = Files.readAllBytes(filePath); + return ServerResponse.ok() + .header("Content-Type", file.getFileType()) + .bodyValue(fileContent); + } catch (Exception e) { + return ServerResponse.notFound().build(); + } + }) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + public Mono previewFileByName(ServerRequest request) { + String fileName = request.pathVariable("fileName"); + return fileService.getAllFiles() + .filter(file -> file.getFileName().equals(fileName)) + .next() + .flatMap(file -> { + try { + Path filePath = Paths.get(file.getFilePath()); + byte[] fileContent = Files.readAllBytes(filePath); + return ServerResponse.ok() + .header("Content-Type", file.getFileType()) + .bodyValue(fileContent); + } catch (Exception e) { + return ServerResponse.notFound().build(); + } + }) + .switchIfEmpty(ServerResponse.notFound().build()); + } + + public Mono deleteFile(ServerRequest request) { + Long id = Long.parseLong(request.pathVariable("id")); + return fileService.deleteFile(id) + .then(ServerResponse.ok().build()) + .onErrorResume(e -> ServerResponse.badRequest().bodyValue(e.getMessage())); + } +}