Browse Source

uploads done

master
esoe 6 months ago
parent
commit
9791da3f7b
  1. 10
      .vscode/launch.json
  2. 1
      explorer_rs/.env
  3. 9
      explorer_rs/docker-compose.yaml
  4. 10
      explorer_rs/dockerfile
  5. 4
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/ExplorerRsApplication.java
  6. 17
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/config/StorageProperties.java
  7. 73
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/controller/FileController.java
  8. 18
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/entity/FileResponse.java
  9. 15
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/exception/FileNotFoundException.java
  10. 11
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/exception/StorageException.java
  11. 107
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/repository/FileSystemStorageService.java
  12. 16
      explorer_rs/src/main/java/ru/molokoin/explorer_rs/repository/StorageService.java
  13. 5
      explorer_rs/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  14. 6
      explorer_rs/src/main/resources/application.yaml
  15. 33
      explorer_rs/src/main/resources/templates/listFiles.html

10
.vscode/launch.json vendored

@ -27,6 +27,16 @@ @@ -27,6 +27,16 @@
"args": "",
"envFile": "${workspaceFolder}/.env",
"vmArgs": " -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=52355 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dspring.jmx.enabled=true -Djava.rmi.server.hostname=localhost -Dspring.application.admin.enabled=true -Dspring.boot.project.name=resource-service-api"
},
{
"type": "java",
"name": "Spring Boot-ExplorerRsApplication<explorer_rs>",
"request": "launch",
"cwd": "${workspaceFolder}",
"mainClass": "ru.molokoin.explorer_rs.ExplorerRsApplication",
"projectName": "explorer_rs",
"args": "",
"envFile": "${workspaceFolder}/.env"
}
]
}

1
explorer_rs/.env

@ -0,0 +1 @@ @@ -0,0 +1 @@
DATA='/app/explorer_rs/uploads'

9
explorer_rs/docker-compose.yaml

@ -1,14 +1,19 @@ @@ -1,14 +1,19 @@
version: "3.7"
services:
resource-service-api:
explorer_rs:
build:
context: ../explorer_rs
dockerfile: dockerfile
image: "explorer_rs"
command: ["java","-jar","/app/explorer_rs-0.1.jar"]
command: ["java","-jar","/app/explorer_rs/explorer_rs-0.1.jar"]
ports:
- 82:8282
restart: unless-stopped
volumes:
- data_explorer_rs:${DATA}
volumes:
data_explorer_rs:
external: true
networks:
default:
external:

10
explorer_rs/dockerfile

@ -1,8 +1,12 @@ @@ -1,8 +1,12 @@
FROM openjdk:17-jdk-alpine
RUN apk update
RUN apk upgrade
COPY target/explorer_rs-0.1.jar /app/explorer_rs-0.1.jar
WORKDIR /app
COPY target/explorer_rs-0.1.jar /app/explorer_rs/explorer_rs-0.1.jar
WORKDIR /app/explorer_rs
# ENTRYPOINT ["java","-jar","/app/resource-service-api-0.1.jar"]
# docker image build -t resource-service-api:latest .
# docker run -d -p80:8181 resource-service-api:latest
# docker run -d -p80:8181 resource-service-api:latest
# Путь к загружаемым на сервер данным:
# /app/explorer_rs/uploads
# Нужно создать для него том, чтобы получить доступ к загружаемым на сервер данным

4
explorer_rs/src/main/java/ru/molokoin/explorer_rs/ExplorerRsApplication.java

@ -2,8 +2,12 @@ package ru.molokoin.explorer_rs; @@ -2,8 +2,12 @@ package ru.molokoin.explorer_rs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import ru.molokoin.explorer_rs.config.StorageProperties;
@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class ExplorerRsApplication {
public static void main(String[] args) {

17
explorer_rs/src/main/java/ru/molokoin/explorer_rs/config/StorageProperties.java

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package ru.molokoin.explorer_rs.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "storage")
public class StorageProperties {
private String location;
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}

73
explorer_rs/src/main/java/ru/molokoin/explorer_rs/controller/FileController.java

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
package ru.molokoin.explorer_rs.controller;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
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.support.ServletUriComponentsBuilder;
import ru.molokoin.explorer_rs.entity.FileResponse;
import ru.molokoin.explorer_rs.repository.StorageService;
@Controller
public class FileController {
private StorageService storageService;
public FileController(StorageService storageService) {
this.storageService = storageService;
}
@GetMapping("/")
public String listAllFiles(Model model) {
model.addAttribute("files", storageService.loadAll().map(
path -> ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/download/")
.path(path.getFileName().toString())
.toUriString())
.collect(Collectors.toList()));
return "listFiles";
}
@GetMapping("/download/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
Resource resource = storageService.loadAsResource(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
@PostMapping("/upload-file")
@ResponseBody
public FileResponse uploadFile(@RequestParam("file") MultipartFile file) {
String name = storageService.store(file);
String uri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/download/")
.path(name)
.toUriString();
return new FileResponse(name, uri, file.getContentType(), file.getSize());
}
@PostMapping("/upload-multiple-files")
@ResponseBody
public List<FileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
return Arrays.stream(files)
.map(file -> uploadFile(file))
.collect(Collectors.toList());
}
}

18
explorer_rs/src/main/java/ru/molokoin/explorer_rs/entity/FileResponse.java

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
package ru.molokoin.explorer_rs.entity;
import lombok.Data;
@Data
public class FileResponse {
private String name;
private String uri;
private String type;
private long size;
public FileResponse(String name, String uri, String type, long size) {
this.name = name;
this.uri = uri;
this.type = type;
this.size = size;
}
}

15
explorer_rs/src/main/java/ru/molokoin/explorer_rs/exception/FileNotFoundException.java

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
package ru.molokoin.explorer_rs.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class FileNotFoundException extends StorageException{
public FileNotFoundException(String message) {
super(message);
}
public FileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

11
explorer_rs/src/main/java/ru/molokoin/explorer_rs/exception/StorageException.java

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
package ru.molokoin.explorer_rs.exception;
public class StorageException extends RuntimeException{
public StorageException(String message) {
super(message);
}
public StorageException(String message, Throwable cause) {
super(message, cause);
}
}

107
explorer_rs/src/main/java/ru/molokoin/explorer_rs/repository/FileSystemStorageService.java

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
package ru.molokoin.explorer_rs.repository;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.PostConstruct;
import ru.molokoin.explorer_rs.config.StorageProperties;
import ru.molokoin.explorer_rs.exception.StorageException;
import ru.molokoin.explorer_rs.exception.FileNotFoundException;
@Service
public class FileSystemStorageService implements StorageService{
private final Path rootLocation;
@Autowired
public FileSystemStorageService(StorageProperties properties) {
this.rootLocation = Paths.get(properties.getLocation());
}
@Override
@PostConstruct
public void init() {
try {
Files.createDirectories(rootLocation);
} catch (IOException e) {
throw new StorageException("Could not initialize storage location", e);
}
}
@Override
public String store(MultipartFile file) {
String filename = StringUtils.cleanPath(file.getOriginalFilename());
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file " + filename);
}
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store file with relative path outside current directory "
+ filename);
}
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, this.rootLocation.resolve(filename),
StandardCopyOption.REPLACE_EXISTING);
}
}
catch (IOException e) {
throw new StorageException("Failed to store file " + filename, e);
}
return filename;
}
@Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.rootLocation, 1)
.filter(path -> !path.equals(this.rootLocation))
.map(this.rootLocation::relativize);
}
catch (IOException e) {
throw new StorageException("Failed to read stored files", e);
}
}
@Override
public Path load(String filename) {
return rootLocation.resolve(filename);
}
@Override
public Resource loadAsResource(String filename) {
try {
Path file = load(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
}
else {
throw new FileNotFoundException(
"Could not read file: " + filename);
}
}catch (MalformedURLException e) {
throw new FileNotFoundException("Could not read file: " + filename, e);
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
}

16
explorer_rs/src/main/java/ru/molokoin/explorer_rs/repository/StorageService.java

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
package ru.molokoin.explorer_rs.repository;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.util.stream.Stream;
public interface StorageService {
void init();
String store(MultipartFile file);
Stream<Path> loadAll();
Path load(String filename);
Resource loadAsResource(String filename);
void deleteAll();
}

5
explorer_rs/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": "Path to local storage for uploaded files"
}]}

6
explorer_rs/src/main/resources/application.yaml

@ -11,3 +11,9 @@ spring: @@ -11,3 +11,9 @@ spring:
url: "jdbc:postgresql://postgres-service:5432/tech-services"
username: tech-services
password: password
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
storage:
location: ./uploads

33
explorer_rs/src/main/resources/templates/listFiles.html

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<h1>Spring Boot File Upload Example</h1>
<hr/>
<h4>Upload Single File:</h4>
<form method="POST" enctype="multipart/form-data" th:action="@{/upload-file}">
<input type="file" name="file"> <br/><br/>
<button type="submit">Submit</button>
</form>
<hr/>
<h4>Upload Multiple Files:</h4>
<form method="POST" enctype="multipart/form-data" th:action="@{/upload-multiple-files}">
<input type="file" name="files" multiple> <br/><br/>
<button type="submit">Submit</button>
</form>
<hr/>
<h2>All Uploaded Files:</h2>
<ul>
<li th:each="file : ${files}">
<a th:href="${file}" target="_blank" th:text="${file}"></a>
</li>
</ul>
</body>
</html>
Loading…
Cancel
Save