Browse Source

simple works. Starts xlsx-view

master
esoe 2 months ago
parent
commit
fcd563cc1f
  1. 10
      face/src/main/resources/application.yaml
  2. 4
      logger/logs/teh.log
  3. 3
      storage/.env
  4. 3
      storage/docker-compose.yaml
  5. 34
      storage/logs/teh.log
  6. 27
      storage/pom.xml
  7. 62
      storage/src/main/java/gsp/technologies/storage/controllers/api/ApiController.java
  8. 38
      storage/src/main/java/gsp/technologies/storage/controllers/api/ApiLink.java
  9. 191
      storage/src/main/java/gsp/technologies/storage/controllers/face/FaceController.java
  10. 36
      storage/src/main/java/gsp/technologies/storage/controllers/face/FaceLink.java
  11. 106
      storage/src/main/java/gsp/technologies/storage/controllers/hello/face/FaceController.java
  12. 7
      storage/src/main/java/gsp/technologies/storage/exceptions/ServiceException.java
  13. 20
      storage/src/main/java/gsp/technologies/storage/models/FileMetadata.java
  14. 4
      storage/src/main/java/gsp/technologies/storage/services/FileService.java
  15. 25
      storage/src/main/java/gsp/technologies/storage/services/FileServiceImpl.java
  16. 5
      storage/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  17. 27
      storage/src/main/resources/application.yaml
  18. 24
      storage/src/main/resources/templates/mainframe.html
  19. 20
      storage/src/main/resources/templates/view-xlsx.html

10
face/src/main/resources/application.yaml

@ -3,8 +3,8 @@ spring: @@ -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: @@ -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
max-swallow-size: 1000MB
max-http-form-post-size: 1000MB
# maxParameterCount: 10000

4
logger/logs/teh.log

@ -125,3 +125,7 @@ @@ -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"}

3
storage/.env

@ -1 +1,2 @@ @@ -1 +1,2 @@
DATA='/app/storage/uploads'
DATA='/app/storage/uploads'
LOGGER_DATA='/app/storage/logs'

3
storage/docker-compose.yaml

@ -13,11 +13,14 @@ services: @@ -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

34
storage/logs/teh.log

@ -0,0 +1,34 @@ @@ -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"}

27
storage/pom.xml

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<version>3.4.0-M3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>gsp.technologies</groupId>
@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@ -55,7 +55,7 @@ @@ -55,7 +55,7 @@
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@ -79,5 +79,24 @@ @@ -79,5 +79,24 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

62
storage/src/main/java/gsp/technologies/storage/controllers/hello/api/ApiController.java → storage/src/main/java/gsp/technologies/storage/controllers/api/ApiController.java

@ -1,11 +1,16 @@ @@ -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; @@ -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 { @@ -34,7 +40,8 @@ public class ApiController {
@GetMapping("/files")
public @ResponseBody
List<FileMetadata> 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 { @@ -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 { @@ -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<FileMetadata> 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<List<FileMetadata>> uploads(@RequestParam("files") MultipartFile[] files) {
LOG.info("POST /files/store-multiple");
List<FileMetadata> 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 { @@ -88,6 +116,7 @@ public class ApiController {
*/
@GetMapping("/files/download")
public ResponseEntity<Resource> 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 { @@ -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 { @@ -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 { @@ -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);
}

38
storage/src/main/java/gsp/technologies/storage/controllers/api/ApiLink.java

@ -0,0 +1,38 @@ @@ -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;
}
}

191
storage/src/main/java/gsp/technologies/storage/controllers/face/FaceController.java

@ -0,0 +1,191 @@ @@ -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<FileMetadata> 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 <FileMetadata>(){})
.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 <FileMetadata>(){})
.block();
}
return FaceLink.MAINFRAME.redirect();
}
@GetMapping("/files/download-file")
@ResponseBody
public ResponseEntity<Resource> 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();
}
}

36
storage/src/main/java/gsp/technologies/storage/controllers/face/FaceLink.java

@ -0,0 +1,36 @@ @@ -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;
}
}

106
storage/src/main/java/gsp/technologies/storage/controllers/hello/face/FaceController.java

@ -1,106 +0,0 @@ @@ -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<Resource> 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";
}
}

7
storage/src/main/java/gsp/technologies/storage/exceptions/ServiceException.java

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
package gsp.technologies.storage.exceptions;
public class ServiceException extends Exception {
public ServiceException(String message) {
super(message);
}
}

20
storage/src/main/java/gsp/technologies/storage/models/FileMetadata.java

@ -93,7 +93,7 @@ public class FileMetadata { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);
}
}

4
storage/src/main/java/gsp/technologies/storage/services/FileService.java

@ -4,6 +4,7 @@ import java.nio.file.Path; @@ -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 { @@ -22,5 +23,6 @@ public interface FileService {
public Path root();
void init();
Resource loadAsResource(String filename);
String store(MultipartFile[] files);
List<FileMetadata> store(MultipartFile[] files);
FileMetadata store(MultipartFile file);
}

25
storage/src/main/java/gsp/technologies/storage/services/FileServiceImpl.java

@ -1,7 +1,6 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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<String> names = new ArrayList<>();
public List<FileMetadata> store(MultipartFile[] files) {
List<FileMetadata> metadata = new ArrayList<>();
for (MultipartFile file : files) {
names.add(store(file));
metadata.add(store(file));
}
return String.join(",", names);
return metadata;
}
}

5
storage/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
{"properties": [{
"name": "storage.location",
"type": "java.lang.String",
"description": "'storage.location' path to storage directory"
}]}

27
storage/src/main/resources/application.yaml

@ -6,6 +6,14 @@ spring: @@ -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: @@ -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: ./

24
storage/src/main/resources/templates/mainframe.html

@ -11,11 +11,14 @@ @@ -11,11 +11,14 @@
</header>
<main>
<div>
<!-- <form th:action="@{/face/v1/upload}" th:method="post" enctype="multipart/form-data">
<h2>Загрузка файлов по одному:</h2>
<form th:action="@{/face/v1/upload-file}" th:method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">send</button>
</form>
<hr> -->
<hr>
<h2>Загрузка файлов пакетом:</h2>
<p>Пакет не должен превышать 1 Гб</p>
<form th:action="@{/face/v1/upload-multiple-files}"
th:method="post"
enctype="multipart/form-data">
@ -38,12 +41,25 @@ @@ -38,12 +41,25 @@
</td>
<!-- <td><a th:href="@{/api/v1/files/{path}(path=${file.path})}">open</a></td> -->
<td>
<a th:href="@{/api/v1/files/download?filename={filename}(filename=${file.name})}">download</a>
<a th:href="@{/face/v1/files/download-file?filename={filename}(filename=${file.name})}">скачать</a>
</td>
<td><a th:href="@{/face/v1/files/delete?filename={filename}(filename=${file.name})}">-</a></td>
<td><a th:href="@{/face/v1/files/delete-file?filename={filename}(filename=${file.name})}">удалить</a></td>
<td><a th:href="@{/face/v1/files/view?filename={filename}(filename=${file.name})}">просмотреть</a></td>
</tr>
</table>
</div>
<hr>
</main>
<footer>
<h1>Заметки</h1>
<ul>
<li>Реализовать отправку данных xlsx файла пользователю, постранично</li>
<li>Реализовать пагинацию. Вывод списка файлов на сервере постранично</li>
<li>Реализовать фильтрацию по расширению, пути к файлу</li>
<li>реализовать создание и просмотр директорий, в соответствии с названиями сервисов, которые размещают данные в хранилище.
Хранение данных сервисов по раздельным директориям</li>
</ul>
</footer>
</body>
</html>

20
storage/src/main/resources/templates/view-xlsx.html

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>xlsx</title>
</head>
<body>
<header>
<h1>VIEW-XLSX</h1>
<hr>
</header>
<main>
<div class="files">
<h2>Просмотр xlsx-контента: <span th:text="${filename}"></span></h2>
</div>
<hr>
</main>
</body>
</html>
Loading…
Cancel
Save