diff --git a/face/src/main/resources/application.yaml b/face/src/main/resources/application.yaml index a97524d..fe56204 100644 --- a/face/src/main/resources/application.yaml +++ b/face/src/main/resources/application.yaml @@ -3,8 +3,8 @@ spring: name: face servlet: multipart: - max-file-size: 50MB - max-request-size: 50MB + max-file-size: 1000MB + max-request-size: 1000MB mvc: hiddenmethod: filter: @@ -30,6 +30,6 @@ server: port: 8383 max-http-request-header-size: 50MB tomcat: - max-swallow-size: 50MB - max-http-form-post-size: 50MB - maxParameterCount: 10000 \ No newline at end of file + max-swallow-size: 1000MB + max-http-form-post-size: 1000MB + # maxParameterCount: 10000 \ No newline at end of file diff --git a/logger/logs/teh.log b/logger/logs/teh.log index 802ecf4..d8107c5 100644 --- a/logger/logs/teh.log +++ b/logger/logs/teh.log @@ -125,3 +125,7 @@ {"@timestamp":"2024-09-27T13:57:17.421197100Z","log.level":"INFO","process.pid":22996,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.logger.LoggerApplicationTests","message":"Starting LoggerApplicationTests using Java 17.0.7 with PID 22996 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\logger)","ecs.version":"8.11"} {"@timestamp":"2024-09-27T13:57:17.423166200Z","log.level":"INFO","process.pid":22996,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.logger.LoggerApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} {"@timestamp":"2024-09-27T13:57:18.678243900Z","log.level":"INFO","process.pid":22996,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.logger.LoggerApplicationTests","message":"Started LoggerApplicationTests in 1.748 seconds (process running for 2.709)","ecs.version":"8.11"} +{"@timestamp":"2024-09-27T14:00:20.940355100Z","log.level":"WARN","process.pid":11536,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer","message":"\r\n\r\nFound multiple occurrences of org.json.JSONObject on the class path:\r\n\r\n\tjar:file:\/C:\/Users\/devuser\/.m2\/repository\/org\/json\/json\/20240303\/json-20240303.jar!\/org\/json\/JSONObject.class\r\n\tjar:file:\/C:\/Users\/devuser\/.m2\/repository\/com\/vaadin\/external\/google\/android-json\/0.0.20131108.vaadin1\/android-json-0.0.20131108.vaadin1.jar!\/org\/json\/JSONObject.class\r\n\r\nYou may wish to exclude one of them to ensure predictable runtime behavior\r\n","ecs.version":"8.11"} +{"@timestamp":"2024-09-27T14:00:20.983001800Z","log.level":"INFO","process.pid":11536,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.logger.LoggerApplicationTests","message":"Starting LoggerApplicationTests using Java 17.0.7 with PID 11536 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\logger)","ecs.version":"8.11"} +{"@timestamp":"2024-09-27T14:00:20.988002700Z","log.level":"INFO","process.pid":11536,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.logger.LoggerApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-09-27T14:00:22.557061300Z","log.level":"INFO","process.pid":11536,"process.thread.name":"main","service.name":"logger","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.logger.LoggerApplicationTests","message":"Started LoggerApplicationTests in 2.121 seconds (process running for 3.224)","ecs.version":"8.11"} diff --git a/storage/.env b/storage/.env index 5cf4acf..552a4fc 100644 --- a/storage/.env +++ b/storage/.env @@ -1 +1,2 @@ -DATA='/app/storage/uploads' \ No newline at end of file +DATA='/app/storage/uploads' +LOGGER_DATA='/app/storage/logs' \ No newline at end of file diff --git a/storage/docker-compose.yaml b/storage/docker-compose.yaml index 3c4ef85..8c6eb0c 100644 --- a/storage/docker-compose.yaml +++ b/storage/docker-compose.yaml @@ -13,11 +13,14 @@ services: restart: unless-stopped volumes: - teh-storage:${DATA} + - teh-logs:${LOGGER_DATA} networks: - teh-net volumes: teh-storage: external: true + teh-logs: + external: true networks: teh-net: name: teh-net diff --git a/storage/logs/teh.log b/storage/logs/teh.log new file mode 100644 index 0000000..db39ba8 --- /dev/null +++ b/storage/logs/teh.log @@ -0,0 +1,34 @@ +{"@timestamp":"2024-10-02T07:04:55.293627400Z","log.level":"INFO","process.pid":9376,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 9376 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T07:04:55.299040500Z","log.level":"INFO","process.pid":9376,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T07:04:57.188504200Z","log.level":"INFO","process.pid":9376,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.342 seconds (process running for 3.454)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T07:56:38.064241800Z","log.level":"INFO","process.pid":22060,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 22060 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T07:56:38.074240900Z","log.level":"INFO","process.pid":22060,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T07:56:39.837714500Z","log.level":"INFO","process.pid":22060,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.217 seconds (process running for 3.283)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T08:16:48.125170400Z","log.level":"INFO","process.pid":22288,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 22288 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T08:16:48.130286200Z","log.level":"INFO","process.pid":22288,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T08:16:49.021858900Z","log.level":"INFO","process.pid":22288,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T08:16:49.918673Z","log.level":"INFO","process.pid":22288,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.226 seconds (process running for 3.275)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:36:42.081332700Z","log.level":"INFO","process.pid":7404,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 7404 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:36:42.087332900Z","log.level":"INFO","process.pid":7404,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:36:42.976191Z","log.level":"INFO","process.pid":7404,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:36:43.854858Z","log.level":"INFO","process.pid":7404,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.274 seconds (process running for 3.386)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:47:09.155604200Z","log.level":"INFO","process.pid":12256,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 12256 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:47:09.160578900Z","log.level":"INFO","process.pid":12256,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:47:10.112966Z","log.level":"INFO","process.pid":12256,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:47:10.955370200Z","log.level":"INFO","process.pid":12256,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.251 seconds (process running for 3.423)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:54:50.824390600Z","log.level":"INFO","process.pid":21356,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 21356 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:54:50.833390800Z","log.level":"INFO","process.pid":21356,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:54:51.886224900Z","log.level":"INFO","process.pid":21356,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T09:54:52.798015700Z","log.level":"INFO","process.pid":21356,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.506 seconds (process running for 3.64)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T10:19:00.457454300Z","log.level":"INFO","process.pid":4544,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 4544 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T10:19:00.463454800Z","log.level":"INFO","process.pid":4544,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T10:19:01.350682300Z","log.level":"INFO","process.pid":4544,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T10:19:02.167066Z","log.level":"INFO","process.pid":4544,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.11 seconds (process running for 3.226)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:13:53.815353Z","log.level":"INFO","process.pid":19852,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 19852 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:13:53.822362600Z","log.level":"INFO","process.pid":19852,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:13:54.776505500Z","log.level":"INFO","process.pid":19852,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:13:55.719635700Z","log.level":"INFO","process.pid":19852,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.36 seconds (process running for 3.438)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:14:51.073276100Z","log.level":"INFO","process.pid":14552,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Starting StorageApplicationTests using Java 17.0.7 with PID 14552 (started by devuser in C:\\Users\\devuser\\Documents\\code\\teh\\storage)","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:14:51.081408400Z","log.level":"INFO","process.pid":14552,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"No active profile set, falling back to 1 default profile: \"default\"","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:14:51.975885900Z","log.level":"INFO","process.pid":14552,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.services.FileServiceImpl","message":"Создание директории root(), для хранения файлов: C:\\Users\\devuser\\Documents\\code\\teh\\storage\\uploads","ecs.version":"8.11"} +{"@timestamp":"2024-10-02T11:14:52.898326Z","log.level":"INFO","process.pid":14552,"process.thread.name":"main","service.name":"face","service.version":"1","service.environment":"Production","service.node.name":"Primary","log.logger":"gsp.technologies.storage.StorageApplicationTests","message":"Started StorageApplicationTests in 2.25 seconds (process running for 3.275)","ecs.version":"8.11"} diff --git a/storage/pom.xml b/storage/pom.xml index abda027..e3c5611 100644 --- a/storage/pom.xml +++ b/storage/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.4 + 3.4.0-M3 gsp.technologies @@ -47,7 +47,7 @@ org.springframework.boot spring-boot-starter-webflux - + org.springframework.boot spring-boot-starter-test @@ -79,5 +79,24 @@ - + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + diff --git a/storage/src/main/java/gsp/technologies/storage/controllers/hello/api/ApiController.java b/storage/src/main/java/gsp/technologies/storage/controllers/api/ApiController.java similarity index 67% rename from storage/src/main/java/gsp/technologies/storage/controllers/hello/api/ApiController.java rename to storage/src/main/java/gsp/technologies/storage/controllers/api/ApiController.java index e25e18c..8fd5235 100644 --- a/storage/src/main/java/gsp/technologies/storage/controllers/hello/api/ApiController.java +++ b/storage/src/main/java/gsp/technologies/storage/controllers/api/ApiController.java @@ -1,11 +1,16 @@ -package gsp.technologies.storage.controllers.hello.api; +package gsp.technologies.storage.controllers.api; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.DeleteMapping; @@ -23,6 +28,7 @@ import gsp.technologies.storage.services.FileService; @Controller @RequestMapping("/api/v1") public class ApiController { + private static final Logger LOG = LoggerFactory.getLogger(ApiController.class); // Сервис для работы с файловой системой private final FileService fs; // Инициализация сервиса для работы с файловой системой @@ -34,7 +40,8 @@ public class ApiController { @GetMapping("/files") public @ResponseBody List getFiles() { - //@RequestParam String path + LOG.info("GET /files"); + LOG.info("Файлов в хранилище: {}", fs.listFiles(fs.root()).size()); Path p = fs.root(); return fs.listFiles(p); } @@ -42,6 +49,7 @@ public class ApiController { @GetMapping("/root") public @ResponseBody String root() { + LOG.info("GET /root"); return fs.root().toString(); } @@ -52,34 +60,54 @@ public class ApiController { * @return * @throws IOException */ - @PostMapping("/files/{filename}") + // @PostMapping("/files/{filename}") + // public @ResponseBody + // String uploadFile(@PathVariable String filename, @RequestParam (name = "content") byte[] content) throws IOException { + // System.out.println("ApiController#uploadFile() ---> OK"); + // return fs.uploadFile(filename, content); + // } + + @PostMapping("/files/store") public @ResponseBody - String uploadFile(@PathVariable String filename, @RequestParam (name = "content") byte[] content) throws IOException { - System.out.println("ApiController#uploadFile() ---> OK"); - return fs.uploadFile(filename, content); + ResponseEntity upload(@RequestParam (name = "file") MultipartFile file) throws IOException { + LOG.info("POST /files/store"); + FileMetadata fm = fs.store(file); + return new ResponseEntity<>(fm, HttpStatus.OK); } @PostMapping( - path = "/files", - consumes = {"multipart/form-data"}) + path = "/files/store-multiple") public @ResponseBody - String store(@RequestParam("files") MultipartFile[] files) { - System.out.println("ApiController#uploadFile() ---> OK"); - return fs.store(files); + ResponseEntity> uploads(@RequestParam("files") MultipartFile[] files) { + LOG.info("POST /files/store-multiple"); + List metas = new ArrayList<>(); + for (MultipartFile file : files) { + metas.add(fs.store(file)); + } + return new ResponseEntity<>(metas, HttpStatus.OK); } - + // Метод для удаления файлов + // @DeleteMapping("/files/delete/{filename}") + // public @ResponseBody + // void deleteFile(@PathVariable String filename) { + // LOG.info("DELETE /files/delete/{filename}"); + // fs.deleteFile(filename); + // } // Метод для удаления файлов - @DeleteMapping("/files/{filename}") + @DeleteMapping("/files/delete") public @ResponseBody - void deleteFile(@PathVariable String filename) { + void delete(@RequestParam("filename") String filename) { + LOG.info("DELETE /files/delete"); fs.deleteFile(filename); } + // Метод для получения содержимого файлов @GetMapping("/files/{filename}") public @ResponseBody byte[] getFile(@PathVariable String filename) { + LOG.info("GET /files/{filename}"); return fs.getFile(filename); } @@ -88,6 +116,7 @@ public class ApiController { */ @GetMapping("/files/download") public ResponseEntity download(@RequestParam(name = "filename") String filename) { + LOG.info("GET /files/download"); Resource resource = fs.loadAsResource(filename); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, @@ -108,6 +137,7 @@ public class ApiController { @PostMapping("/files/create") public @ResponseBody String createFile(@RequestParam String path, @RequestParam String name, @RequestParam byte[] content) { + LOG.info("POST /files/create"); return fs.createFile(path, name, content); } /** @@ -116,9 +146,10 @@ public class ApiController { * @param path путь к директории, в которой нужно создать директорию * @param name имя новой директории */ - @PostMapping("/dirs") + @PostMapping("/dirs/create") public @ResponseBody void createDir(@RequestParam String path, @RequestParam String name) { + LOG.info("POST /dirs/create"); // Создаем директорию с указанным именем в указанной директории. fs.createDirectory(path, name); } @@ -130,6 +161,7 @@ public class ApiController { @DeleteMapping("/dirs/{name}") public @ResponseBody void deleteDir(@PathVariable String name) { + LOG.info("DELETE /dirs/{name}"); fs.deleteDirectory(name); } diff --git a/storage/src/main/java/gsp/technologies/storage/controllers/api/ApiLink.java b/storage/src/main/java/gsp/technologies/storage/controllers/api/ApiLink.java new file mode 100644 index 0000000..dfaf0d3 --- /dev/null +++ b/storage/src/main/java/gsp/technologies/storage/controllers/api/ApiLink.java @@ -0,0 +1,38 @@ +package gsp.technologies.storage.controllers.api; + +public enum ApiLink { + HOME("/api/v1"), + FILES("/api/v1/files"), + ROOT ("/api/v1/root"), + STORE_FILE ("/api/v1/files/store"), + STORE_MULTIPLE ("/api/v1/files/store-multiple"), + DELETE_FILE ("/api/v1/files/delete"), + DOWNLOAD_FILE ("/api/v1/files/download"), + CREATE_EMPTY_FILE ("/api/v1/files/create"), + CREATE_DIRECTORY ("/api/v1/dirs/create"), + DELETE_DIRECTORY ("/api/v1/dirs/delete"); + + private String link; + + ApiLink(String link) { + this.link = link; + } + + /** + * Получение локального адреса + * @return the link + */ + public String get() { + return link; + } + + + /** + * Получение полного адреса + * @return full link + */ + public String full(){ + return "http://storage:8282" + link; + } + +} diff --git a/storage/src/main/java/gsp/technologies/storage/controllers/face/FaceController.java b/storage/src/main/java/gsp/technologies/storage/controllers/face/FaceController.java new file mode 100644 index 0000000..f6e8575 --- /dev/null +++ b/storage/src/main/java/gsp/technologies/storage/controllers/face/FaceController.java @@ -0,0 +1,191 @@ +package gsp.technologies.storage.controllers.face; + +import java.io.IOException; +import java.net.URI; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.MultipartBodyBuilder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import gsp.technologies.storage.controllers.api.ApiLink; +import gsp.technologies.storage.models.FileMetadata; +import jakarta.servlet.http.HttpServletResponse; + +@Controller +@RequestMapping("/face/v1") +public class FaceController { + private static final Logger LOG = LoggerFactory.getLogger(FaceController.class); + private final WebClient client; + + @Autowired + public FaceController(WebClient webClient) { + this.client = webClient; + } + + /** + * Возвращает форму для загрузки файлов на сервер + * + * @return thymeleaf-страницу "mainframe" + */ + @GetMapping("/mainframe") + public String getMainframe(Model model) { + LOG.info("GET /mainframe"); + List files = client.get() + .uri(ApiLink.FILES.full()) + .retrieve() + .bodyToFlux(FileMetadata.class) + .collectList() + .block(); + model.addAttribute("files", files); + return "mainframe"; + } + + /** + * Обрабатывает загрузку файлов на сервер и перенаправляет пользователя на страницу загруженного файла + * + * @param file файл для загрузки + * @return перенаправляет пользователя на страницу загрузки файлов + */ + @PostMapping( + path = "/upload-file") + public String uploadFile(@RequestParam("file") MultipartFile file){ + LOG.info("POST /upload-file"); + System.out.println("Загружаем на сервер файл: " + file.getOriginalFilename()); + UriComponents uriComponents = UriComponentsBuilder + .fromUriString(ApiLink.STORE_FILE.full()) + .queryParam("file", file) + .encode() + .build(); + URI url = uriComponents.toUri(); + + MultipartBodyBuilder builder = new MultipartBodyBuilder(); + builder.part("file", file.getResource(), MediaType.MULTIPART_FORM_DATA); + + FileMetadata fm = client.method(HttpMethod.POST) + .uri(url) + .body(BodyInserters + .fromMultipartData(builder.build())) + .retrieve() + .bodyToMono(new ParameterizedTypeReference (){}) + .block(); + + System.out.println("File uploaded successfully: " + file.getOriginalFilename()); + System.out.println("File metadata: " + fm); + + return FaceLink.MAINFRAME.redirect(); + } + + + @PostMapping("/upload-multiple-files") + public String uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { + LOG.info("POST /upload-multiple-files"); + for (MultipartFile file : files) { + UriComponents uriComponents = UriComponentsBuilder + .fromUriString(ApiLink.STORE_FILE.full()) + .queryParam("file", file) + .encode() + .build(); + URI url = uriComponents.toUri(); + + MultipartBodyBuilder builder = new MultipartBodyBuilder(); + builder.part("file", file.getResource(), MediaType.MULTIPART_FORM_DATA); + + FileMetadata fm = client.method(HttpMethod.POST) + .uri(url) + .body(BodyInserters + .fromMultipartData(builder.build())) + .retrieve() + .bodyToMono(new ParameterizedTypeReference (){}) + .block(); + } + return FaceLink.MAINFRAME.redirect(); + } + + @GetMapping("/files/download-file") + @ResponseBody + public ResponseEntity download(@RequestParam(name = "filename") String filename) { + LOG.info("GET /files/download-file"); + UriComponents uriComponents = UriComponentsBuilder + .fromUriString(ApiLink.DOWNLOAD_FILE.full()) + .queryParam("filename", filename) + .encode() + .build(); + URI url = uriComponents.toUri(); + Resource resource = client.get() + .uri(url) + .retrieve() + .bodyToMono(Resource.class) + .block(); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + /** + * Удаление файла с сервера + * @param filename + * @return + */ + @GetMapping("/files/delete-file") + public String delete(HttpServletResponse response, @RequestParam(name = "filename") String filename) { + LOG.info("GET /files/delete-file"); + UriComponents uriComponents = UriComponentsBuilder + .fromUriString(ApiLink.DELETE_FILE.full()) + .queryParam("filename", filename) + .encode() + .build(); + URI url = uriComponents.toUri(); + + client.delete() + .uri(url) + .retrieve() + .toBodilessEntity() + .block(); + + return FaceLink.MAINFRAME.redirect(); + } + + /** + * Определяем расширение файла и если это xlsx, + * отображаем страницу с содержимым xlsx-файла + * @param filename + * @return + */ + @GetMapping("/files/view") + public String view(Model model, + @RequestParam(name = "filename") String filename) { + LOG.info("GET /view"); + + if (FileMetadata.ext(filename).toLowerCase().equals("xlsx")) { + LOG.info("Файл {} является xlsx-файлом", filename); + model.addAttribute("filename", filename); + return "view-xlsx"; + } + + //возвращаем пользователя на главную страницу + LOG.info("Файл {} не является xlsx-файлом. Возвращаем пользователя на главную страницу", filename); + return FaceLink.MAINFRAME.redirect(); + } +} \ No newline at end of file diff --git a/storage/src/main/java/gsp/technologies/storage/controllers/face/FaceLink.java b/storage/src/main/java/gsp/technologies/storage/controllers/face/FaceLink.java new file mode 100644 index 0000000..e2c8e4d --- /dev/null +++ b/storage/src/main/java/gsp/technologies/storage/controllers/face/FaceLink.java @@ -0,0 +1,36 @@ +package gsp.technologies.storage.controllers.face; + +public enum FaceLink { + HOME("/face/v1"), + MAINFRAME("/face/v1/mainframe"), + UPLOAD_FILE("/face/v1/upload-file"), + UPLOAD_MULTIPLE_FILES("/face/v1/upload-multiple-files"), + DOWNLOAD_FILE("/face/v1/files/download-file"), + DELETE_FILE("/face/v1/files/delete-file"); + + private String link; + + FaceLink(String link) { + this.link = link; + } + + /** + * Получение локального адреса + * @return the link + */ + public String get() { + return link; + } + + public String redirect(){ + return "redirect:" + link; + } + + /** + * Получение полного адреса + * @return + */ + public String full() { + return "http://storage:8282" + link; + } +} diff --git a/storage/src/main/java/gsp/technologies/storage/controllers/hello/face/FaceController.java b/storage/src/main/java/gsp/technologies/storage/controllers/hello/face/FaceController.java deleted file mode 100644 index 08e2d2b..0000000 --- a/storage/src/main/java/gsp/technologies/storage/controllers/hello/face/FaceController.java +++ /dev/null @@ -1,106 +0,0 @@ -package gsp.technologies.storage.controllers.hello.face; - -import java.io.IOException; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.ModelAndView; - -import gsp.technologies.storage.services.RemoteFilesService; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -@Controller -@RequestMapping("/face/v1") -public class FaceController { - private final RemoteFilesService fs; - - @Autowired - public FaceController(RemoteFilesService fs) { - this.fs = fs; - } - - /** - * Возвращает форму для загрузки файлов на сервер - * - * @return thymeleaf-страницу "mainframe" - */ - @GetMapping("/mainframe") - public String getMainframe(Model model) { - model.addAttribute("files", fs.listFiles(fs.root())); - return "mainframe"; - } - - /** - * Обрабатывает загрузку файлов на сервер и перенаправляет пользователя на страницу загруженного файла - * - * @param file файл для загрузки - * @return перенаправляет пользователя на страницу загрузки файлов - */ - @PostMapping("/upload") - public void uploadFile(@RequestParam("file") MultipartFile file) { - System.out.println("FaceController#uploadFile(): OK"); - // Загрузка файлов на сервер и получение их метаданных - try { - fs.uploadFile(file); - } catch (Exception e) { - System.out.println("Error uploading file: " + e.getMessage()); - } - - // Перенаправление пользователя на страницу загруженного файла - // return "redirect:/face/v1/mainframe"; - } - - @PostMapping("/upload-multiple-files") - @ResponseBody - public ModelAndView uploadMultipleFiles(HttpServletResponse response, @RequestParam("files") MultipartFile[] files) { - // for (MultipartFile file : files) { - // uploadFile(file); - // } - fs.upload(files); - try { - response.sendRedirect("/face/v1/mainframe"); - } catch (IOException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - System.out.println("Error redirecting: " + e.getMessage()); - } - return null; - // return "redirect:/face/v1/mainframe"; - } - - // @PostMapping("/store") - - - - // @PostMapping("/store-multiple-files") - - - @GetMapping("/download") - @ResponseBody - public ResponseEntity download(@RequestParam(name = "filename") String filename) { - Resource resource = fs.loadAsResource(filename); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, - "attachment; filename=\"" + resource.getFilename() + "\"") - .body(resource); - } - - @GetMapping("/files/delete") - @ResponseBody - public String delete(@RequestParam(name = "filename") String filename) { - fs.deleteFile(filename); - return "redirect:/mainframe"; - } -} \ No newline at end of file diff --git a/storage/src/main/java/gsp/technologies/storage/exceptions/ServiceException.java b/storage/src/main/java/gsp/technologies/storage/exceptions/ServiceException.java new file mode 100644 index 0000000..43dd308 --- /dev/null +++ b/storage/src/main/java/gsp/technologies/storage/exceptions/ServiceException.java @@ -0,0 +1,7 @@ +package gsp.technologies.storage.exceptions; + +public class ServiceException extends Exception { + public ServiceException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/storage/src/main/java/gsp/technologies/storage/models/FileMetadata.java b/storage/src/main/java/gsp/technologies/storage/models/FileMetadata.java index 33a9fa3..9e952c7 100644 --- a/storage/src/main/java/gsp/technologies/storage/models/FileMetadata.java +++ b/storage/src/main/java/gsp/technologies/storage/models/FileMetadata.java @@ -93,7 +93,7 @@ public class FileMetadata { try { return Files.size(Paths.get(path)); } catch (IOException e) { - System.out.println("FileMetadata.initSize :: Error reading file size: " + e.getMessage()); + // System.out.println("FileMetadata.initSize :: Error reading file size: " + e.getMessage()); return 0L; } } @@ -107,7 +107,7 @@ public class FileMetadata { try { return Files.getLastModifiedTime(Paths.get(path)).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); } catch (IOException e) { - System.out.println("FileMetadata.initCreatedDate :: Error reading created date: " + e.getMessage()); + // System.out.println("FileMetadata.initCreatedDate :: Error reading created date: " + e.getMessage()); return LocalDateTime.now(); } } @@ -120,7 +120,7 @@ public class FileMetadata { try { return Files.getLastModifiedTime(Paths.get(path)).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); } catch (IOException e) { - System.out.println("FileMetadata.initLastModifiedDate :: Error reading last modified date: " + e.getMessage()); + // System.out.println("FileMetadata.initLastModifiedDate :: Error reading last modified date: " + e.getMessage()); return LocalDateTime.now(); } } @@ -133,7 +133,7 @@ public class FileMetadata { try { return Files.getOwner(Paths.get(path)).getName(); } catch (IOException e) { - System.out.println("FileMetadata.initOwner :: Error reading owner: " + e.getMessage()); + // System.out.println("FileMetadata.initOwner :: Error reading owner: " + e.getMessage()); return ""; } } @@ -159,7 +159,7 @@ public class FileMetadata { try { return Files.getPosixFilePermissions(Paths.get(path)).toString(); } catch (IOException e) { - System.out.println("FileMetadata.initPermissions :: Error reading permissions: " + e.getMessage()); + // System.out.println("FileMetadata.initPermissions :: Error reading permissions: " + e.getMessage()); return ""; } } @@ -213,4 +213,14 @@ public class FileMetadata { } return children; } + public static String ext(String filename){ + if (filename == null || filename.isEmpty()) { + return ""; + } + int lastDotIndex = filename.lastIndexOf('.'); + if (lastDotIndex == -1) { + return ""; + } + return filename.substring(lastDotIndex + 1); + } } diff --git a/storage/src/main/java/gsp/technologies/storage/services/FileService.java b/storage/src/main/java/gsp/technologies/storage/services/FileService.java index 73181b7..4e1d878 100644 --- a/storage/src/main/java/gsp/technologies/storage/services/FileService.java +++ b/storage/src/main/java/gsp/technologies/storage/services/FileService.java @@ -4,6 +4,7 @@ import java.nio.file.Path; import java.util.List; import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; import org.springframework.web.multipart.MultipartFile; import gsp.technologies.storage.models.FileMetadata; @@ -22,5 +23,6 @@ public interface FileService { public Path root(); void init(); Resource loadAsResource(String filename); - String store(MultipartFile[] files); + List store(MultipartFile[] files); + FileMetadata store(MultipartFile file); } \ No newline at end of file diff --git a/storage/src/main/java/gsp/technologies/storage/services/FileServiceImpl.java b/storage/src/main/java/gsp/technologies/storage/services/FileServiceImpl.java index 3a2828b..11546e0 100644 --- a/storage/src/main/java/gsp/technologies/storage/services/FileServiceImpl.java +++ b/storage/src/main/java/gsp/technologies/storage/services/FileServiceImpl.java @@ -1,7 +1,6 @@ package gsp.technologies.storage.services; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -13,17 +12,21 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import gsp.technologies.storage.config.StorageConfig; +import gsp.technologies.storage.controllers.api.ApiController; import gsp.technologies.storage.models.FileMetadata; import jakarta.annotation.PostConstruct; @Service public class FileServiceImpl implements FileService { + private static final Logger LOG = LoggerFactory.getLogger(FileServiceImpl.class); private final Path storageLocation; public FileServiceImpl(StorageConfig config) { this.storageLocation = Paths.get(config.getLocation()) @@ -39,10 +42,12 @@ public class FileServiceImpl implements FileService { public void init() { try { // Создание директории для хранения файлов, если она не существует + LOG.info("Создание директории root(), для хранения файлов: {}", storageLocation); Files.createDirectories(storageLocation); } catch (IOException e) { // Печатает в консоль сообщение об ошибке при создании директории - System.out.println("Ошибка создания директории для хранения файлов: " + e.getMessage()); + // System.out.println("Ошибка создания директории для хранения файлов: " + e.getMessage()); + LOG.info("Директория {}, уже существует: {}", storageLocation, e.getMessage()); } } @@ -103,12 +108,13 @@ public class FileServiceImpl implements FileService { @Override public void deleteFile(String filename) { // Создаем объект файла с указанным именем. - File file = new File(filename); + File file = new File(storageLocation + "/" +filename); // Если файл существует, удаляем его. if (file.exists()) { file.delete(); } } + /** * Возвращает содержимое файла, указанного в параметре filename. * Если файл не найден, возвращает пустой массив байтов. @@ -232,7 +238,7 @@ public class FileServiceImpl implements FileService { return storageLocation.resolve(filename); } - public String store(MultipartFile file) { + public FileMetadata store(MultipartFile file) { try (InputStream inputStream = file.getInputStream()) { Files.copy(inputStream, root().resolve(file.getOriginalFilename()), StandardCopyOption.REPLACE_EXISTING); @@ -240,16 +246,15 @@ public class FileServiceImpl implements FileService { catch (IOException e) { System.out.println("Не удалось сохранить файл " + file.getOriginalFilename()); } - return file.getOriginalFilename(); + return new FileMetadata(file.getOriginalFilename()); } @Override - public String store(MultipartFile[] files) { - List names = new ArrayList<>(); + public List store(MultipartFile[] files) { + List metadata = new ArrayList<>(); for (MultipartFile file : files) { - names.add(store(file)); + metadata.add(store(file)); } - return String.join(",", names); + return metadata; } - } \ No newline at end of file diff --git a/storage/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/storage/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..4196694 --- /dev/null +++ b/storage/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,5 @@ +{"properties": [{ + "name": "storage.location", + "type": "java.lang.String", + "description": "'storage.location' path to storage directory" +}]} \ No newline at end of file diff --git a/storage/src/main/resources/application.yaml b/storage/src/main/resources/application.yaml index 7d11c33..8f573e3 100644 --- a/storage/src/main/resources/application.yaml +++ b/storage/src/main/resources/application.yaml @@ -6,6 +6,14 @@ spring: max-file-size: 1000MB max-request-size: 1000MB enabled: true + mvc: + hiddenmethod: + filter: + enabled: true + format: + date: yyyy-MM-dd + date-time: yyyy-MM-dd HH:mm:ss + time: HH:mm:ss jpa: hibernate: ddl-auto: update @@ -14,9 +22,26 @@ spring: url: "jdbc:postgresql://base:5432/teh" username: teh password: teh - +logging: + structured: + ecs: + service: + name: face + version: 1 + environment: Production + node-name: Primary + format: + file: ecs + file: + name: logs/teh.log + # path: server: port: 8282 + max-http-request-header-size: 50MB + tomcat: + max-swallow-size: 1000MB + max-http-form-post-size: 1000MB + # maxParameterCount: 10000 storage: location: ./uploads # root: ./ diff --git a/storage/src/main/resources/templates/mainframe.html b/storage/src/main/resources/templates/mainframe.html index 75d82d9..267ffaf 100644 --- a/storage/src/main/resources/templates/mainframe.html +++ b/storage/src/main/resources/templates/mainframe.html @@ -11,11 +11,14 @@
- +
+

Загрузка файлов пакетом:

+

Пакет не должен превышать 1 Гб

@@ -38,12 +41,25 @@ - download + скачать - - + удалить + просмотреть
+
+
+

Заметки

+
    +
  • Реализовать отправку данных xlsx файла пользователю, постранично
  • +
  • Реализовать пагинацию. Вывод списка файлов на сервере постранично
  • +
  • Реализовать фильтрацию по расширению, пути к файлу
  • +
  • реализовать создание и просмотр директорий, в соответствии с названиями сервисов, которые размещают данные в хранилище. + Хранение данных сервисов по раздельным директориям
  • +
+ +
\ No newline at end of file diff --git a/storage/src/main/resources/templates/view-xlsx.html b/storage/src/main/resources/templates/view-xlsx.html new file mode 100644 index 0000000..80f3305 --- /dev/null +++ b/storage/src/main/resources/templates/view-xlsx.html @@ -0,0 +1,20 @@ + + + + xlsx + + + +
+

VIEW-XLSX

+
+
+
+ +
+

Просмотр xlsx-контента:

+
+
+
+ + \ No newline at end of file