esoe
6 months ago
20 changed files with 131 additions and 447 deletions
@ -1,17 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,74 +0,0 @@ |
|||||||
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.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.support.ServletUriComponentsBuilder; |
|
||||||
|
|
||||||
import ru.molokoin.explorer_rs.entity.FileResponse; |
|
||||||
import ru.molokoin.explorer_rs.service.StorageService; |
|
||||||
|
|
||||||
@Controller |
|
||||||
@RequestMapping(path = "/explorer_rs") |
|
||||||
public class FileController { |
|
||||||
private StorageService storageService; |
|
||||||
|
|
||||||
public FileController(StorageService storageService) { |
|
||||||
this.storageService = storageService; |
|
||||||
} |
|
||||||
|
|
||||||
@GetMapping("/list") |
|
||||||
public String listAllFiles(Model model) { |
|
||||||
model.addAttribute("files", storageService.loadAll().map( |
|
||||||
path -> ServletUriComponentsBuilder.fromCurrentContextPath() |
|
||||||
.path("/explorer_rs/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()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,62 +0,0 @@ |
|||||||
package ru.molokoin.explorer_rs.controller; |
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||||
import org.springframework.http.HttpStatus; |
|
||||||
import org.springframework.http.MediaType; |
|
||||||
import org.springframework.http.ResponseEntity; |
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping; |
|
||||||
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.RequestBody; |
|
||||||
import org.springframework.web.bind.annotation.RequestMapping; |
|
||||||
import org.springframework.web.bind.annotation.RestController; |
|
||||||
|
|
||||||
import ru.molokoin.explorer_rs.repository.PackageFace; |
|
||||||
import ru.molokoin.explorer_rs.entity.Package; |
|
||||||
|
|
||||||
@RestController |
|
||||||
@RequestMapping(path = "/", consumes = {"*/*"}) |
|
||||||
public class PackageController { |
|
||||||
@Autowired |
|
||||||
private PackageFace repo; |
|
||||||
|
|
||||||
@GetMapping("/package/list") |
|
||||||
public ResponseEntity<?> getPackages(){ |
|
||||||
return new ResponseEntity<>(repo.findAll(), HttpStatus.OK); |
|
||||||
} |
|
||||||
|
|
||||||
@GetMapping("/package/{id}") |
|
||||||
public ResponseEntity<?> getPackageByID(@PathVariable Integer id){ |
|
||||||
return new ResponseEntity<>(repo.findPackageById(id), HttpStatus.OK); |
|
||||||
} |
|
||||||
|
|
||||||
@PostMapping(path = "/package/create", |
|
||||||
consumes = MediaType.APPLICATION_JSON_VALUE, |
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE) |
|
||||||
public ResponseEntity<?> savePackage(@RequestBody Package pack) { |
|
||||||
repo.save(pack); |
|
||||||
return new ResponseEntity<>(pack, HttpStatus.CREATED); |
|
||||||
} |
|
||||||
|
|
||||||
@PostMapping(path = "/package/update/{id}", |
|
||||||
consumes = MediaType.APPLICATION_JSON_VALUE, |
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE) |
|
||||||
public ResponseEntity<?> updatePackage(@PathVariable Integer id, @RequestBody Package pack) { |
|
||||||
Package p = repo.findPackageById(id); |
|
||||||
p.setTitle(pack.getTitle()); |
|
||||||
p.setOrigin_title(pack.getOrigin_title()); |
|
||||||
p.setType(pack.getType()); |
|
||||||
p.setExtension(pack.getExtension()); |
|
||||||
p.setDate(pack.getDate()); |
|
||||||
repo.save(p); |
|
||||||
return new ResponseEntity<>(repo.findPackageById(id), HttpStatus.CREATED); |
|
||||||
} |
|
||||||
|
|
||||||
@DeleteMapping("/package/delete/{id}") |
|
||||||
public ResponseEntity<?> deletePackage(@PathVariable Long id){ |
|
||||||
Package p = repo.findPackageById(id); |
|
||||||
repo.delete(p); |
|
||||||
return new ResponseEntity<>("Запись id#" + id + " удалена ... ", HttpStatus.OK); |
|
||||||
} |
|
||||||
} |
|
@ -1,10 +1,41 @@ |
|||||||
package ru.molokoin.explorer_rs.controller; |
package ru.molokoin.explorer_rs.controller; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
import org.springframework.stereotype.Controller; |
import org.springframework.stereotype.Controller; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
import org.springframework.web.bind.annotation.RequestMapping; |
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
import ru.molokoin.explorer_rs.service.StorageFace; |
||||||
|
import ru.molokoin.explorer_rs.entity.Document;; |
||||||
|
|
||||||
@Controller |
/** |
||||||
@RequestMapping(path = "/stroage") |
* Контроллер предоставляющий http-api |
||||||
|
* для доступа к файлам хранилища данных |
||||||
|
* |
||||||
|
*/ |
||||||
|
@RestController |
||||||
|
@RequestMapping(path = "/", consumes = {"*/*"}) |
||||||
public class StorageController { |
public class StorageController { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private StorageFace storage; |
||||||
|
|
||||||
|
// public StorageController(StorageFace storage){
|
||||||
|
// this.storage = storage;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** |
||||||
|
* Получение перечня документов размещенных в хранилище |
||||||
|
* - перечень формируется на основании сканирования файловой системы |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@GetMapping(path = "/storage/list") |
||||||
|
public ResponseEntity<List<Document>> listStorage(){ |
||||||
|
return new ResponseEntity<>(storage.list(), HttpStatus.OK); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,39 +1,15 @@ |
|||||||
package ru.molokoin.explorer_rs.entity; |
package ru.molokoin.explorer_rs.entity; |
||||||
|
|
||||||
import jakarta.persistence.Entity; |
|
||||||
import jakarta.persistence.GeneratedValue; |
|
||||||
import jakarta.persistence.GenerationType; |
|
||||||
import jakarta.persistence.Id; |
|
||||||
import lombok.AllArgsConstructor; |
import lombok.AllArgsConstructor; |
||||||
import lombok.Data; |
import lombok.Data; |
||||||
import lombok.NoArgsConstructor; |
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
@NoArgsConstructor |
@NoArgsConstructor |
||||||
@AllArgsConstructor |
@AllArgsConstructor |
||||||
@Entity |
|
||||||
@Data |
@Data |
||||||
public class Document { |
public class Document extends Object{ |
||||||
@Id |
|
||||||
@GeneratedValue(strategy=GenerationType.AUTO) |
|
||||||
private long id; |
|
||||||
private String name;//наименование файла в файловой системе ({id}.{extension})
|
private String name;//наименование файла в файловой системе ({id}.{extension})
|
||||||
private String name_origin;//оригинальное название загруженного файл
|
|
||||||
private String path;//путь в файловой системе (root/{id}.{extension})
|
private String path;//путь в файловой системе (root/{id}.{extension})
|
||||||
private String extension;//расширение файла
|
private String extension;//расширение файла
|
||||||
private long size;//размер файла
|
private long size;//размер файла
|
||||||
|
|
||||||
/** |
|
||||||
* @param name |
|
||||||
* @param name_origin |
|
||||||
* @param path |
|
||||||
* @param extension |
|
||||||
* @param size |
|
||||||
*/ |
|
||||||
public Document(String name, String name_origin, String path, String extension, long size) { |
|
||||||
this.name = name; |
|
||||||
this.name_origin = name_origin; |
|
||||||
this.path = path; |
|
||||||
this.extension = extension; |
|
||||||
this.size = size; |
|
||||||
} |
|
||||||
} |
} |
||||||
|
@ -1,18 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
} |
|
@ -1,48 +0,0 @@ |
|||||||
package ru.molokoin.explorer_rs.entity; |
|
||||||
|
|
||||||
import java.util.Date; |
|
||||||
|
|
||||||
import jakarta.persistence.Entity; |
|
||||||
import jakarta.persistence.GeneratedValue; |
|
||||||
import jakarta.persistence.GenerationType; |
|
||||||
import jakarta.persistence.Id; |
|
||||||
import lombok.AllArgsConstructor; |
|
||||||
import lombok.Data; |
|
||||||
import lombok.NoArgsConstructor; |
|
||||||
|
|
||||||
/** |
|
||||||
* Обертка для хранения данных о загружаемом пакете |
|
||||||
*/ |
|
||||||
@NoArgsConstructor |
|
||||||
@AllArgsConstructor |
|
||||||
@Entity |
|
||||||
@Data |
|
||||||
public class Package { |
|
||||||
@Id |
|
||||||
@GeneratedValue(strategy=GenerationType.AUTO) |
|
||||||
private long id; |
|
||||||
private String title;// Наименование файла в файловой системе
|
|
||||||
private String origin_title;// Наименование загружаемого файла
|
|
||||||
private String type;// Наименование загружаемой формы
|
|
||||||
private String extension;// Расширение файла
|
|
||||||
private Date date;// Время загрузки пакета на сервер
|
|
||||||
|
|
||||||
// Пока не работает блок авторизации, учет автора безсмысленен
|
|
||||||
// private Person author;// Пользователь, загрузивший файл
|
|
||||||
|
|
||||||
/** |
|
||||||
* Конструктор без id |
|
||||||
* @param title |
|
||||||
* @param origin_title |
|
||||||
* @param type |
|
||||||
* @param extension |
|
||||||
* @param date |
|
||||||
*/ |
|
||||||
public Package(String title, String origin_title, String type, String extension, Date date) { |
|
||||||
this.title = title; |
|
||||||
this.origin_title = origin_title; |
|
||||||
this.type = type; |
|
||||||
this.extension = extension; |
|
||||||
this.date = date; |
|
||||||
} |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
package ru.molokoin.explorer_rs.repository; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
import org.springframework.data.repository.ListCrudRepository; |
|
||||||
import org.springframework.stereotype.Repository; |
|
||||||
|
|
||||||
import ru.molokoin.explorer_rs.entity.Document; |
|
||||||
|
|
||||||
@Repository |
|
||||||
public interface DocumentFace extends ListCrudRepository<Document, Long>{ |
|
||||||
List<Document> findAll(); |
|
||||||
Document findDocumentById(long id); |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
package ru.molokoin.explorer_rs.repository; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
import org.springframework.data.repository.ListCrudRepository; |
|
||||||
import org.springframework.stereotype.Repository; |
|
||||||
|
|
||||||
import ru.molokoin.explorer_rs.entity.Package; |
|
||||||
|
|
||||||
@Repository |
|
||||||
public interface PackageFace extends ListCrudRepository<Package, Long>{ |
|
||||||
List<Package> findAll(); |
|
||||||
Package findPackageById(long id); |
|
||||||
} |
|
@ -1,5 +1,12 @@ |
|||||||
package ru.molokoin.explorer_rs.repository; |
package ru.molokoin.explorer_rs.repository; |
||||||
|
|
||||||
public interface RecordFace { |
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.data.repository.ListCrudRepository; |
||||||
|
import org.springframework.stereotype.Repository; |
||||||
|
|
||||||
|
@Repository |
||||||
|
public interface RecordFace extends ListCrudRepository<Record, Long>{ |
||||||
|
List<Record> findAll(); |
||||||
|
Record findRecordById(long id); |
||||||
} |
} |
||||||
|
@ -1,107 +0,0 @@ |
|||||||
package ru.molokoin.explorer_rs.service; |
|
||||||
|
|
||||||
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()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package ru.molokoin.explorer_rs.service; |
|
||||||
|
|
||||||
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(); |
|
||||||
} |
|
@ -1,5 +0,0 @@ |
|||||||
{"properties": [{ |
|
||||||
"name": "storage.location", |
|
||||||
"type": "java.lang.String", |
|
||||||
"description": "Path to local storage for uploaded files" |
|
||||||
}]} |
|
Loading…
Reference in new issue