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()));
+ }
+}