From a601eab65859cee10680e5fbbbd7faf7e2d20a1f Mon Sep 17 00:00:00 2001 From: Kai Volland Date: Mon, 3 May 2021 12:15:24 +0200 Subject: [PATCH] Allows saving of files to filesystem --- .../db/migration/V0.8.0__FilePath.sql | 1 + .../shogun/properties/UploadProperties.java | 5 +- .../src/main/resources/application-base.yml | 2 + .../lib/controller/BaseFileController.java | 72 +++++++++++++++++-- .../de/terrestris/shogun/lib/model/File.java | 4 ++ .../shogun/lib/service/BaseFileService.java | 12 +++- .../shogun/lib/service/FileService.java | 39 +++++++++- .../shogun/lib/service/ImageFileService.java | 7 ++ 8 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 shogun-boot/src/main/resources/db/migration/V0.8.0__FilePath.sql diff --git a/shogun-boot/src/main/resources/db/migration/V0.8.0__FilePath.sql b/shogun-boot/src/main/resources/db/migration/V0.8.0__FilePath.sql new file mode 100644 index 000000000..dd396c318 --- /dev/null +++ b/shogun-boot/src/main/resources/db/migration/V0.8.0__FilePath.sql @@ -0,0 +1 @@ +ALTER TABLE shogun.files ADD COLUMN IF NOT EXISTS path text; diff --git a/shogun-config/src/main/java/de/terrestris/shogun/properties/UploadProperties.java b/shogun-config/src/main/java/de/terrestris/shogun/properties/UploadProperties.java index 101bcc3e6..be1714c3b 100644 --- a/shogun-config/src/main/java/de/terrestris/shogun/properties/UploadProperties.java +++ b/shogun-config/src/main/java/de/terrestris/shogun/properties/UploadProperties.java @@ -20,7 +20,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; @Data @Configuration @@ -33,4 +32,8 @@ public class UploadProperties { @NestedConfigurationProperty private ImageFileUploadProperties image; + private String path; + + private String maxSize; + } diff --git a/shogun-config/src/main/resources/application-base.yml b/shogun-config/src/main/resources/application-base.yml index 63e00f908..e82f2a26c 100644 --- a/shogun-config/src/main/resources/application-base.yml +++ b/shogun-config/src/main/resources/application-base.yml @@ -134,3 +134,5 @@ upload: - image/png - image/svg+xml - image/tiff + path: + maxSize: diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseFileController.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseFileController.java index f1cc8427c..734b1eac4 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseFileController.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/controller/BaseFileController.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.GenericTypeResolver; @@ -38,6 +39,9 @@ public abstract class BaseFileController, S exte protected final Logger LOG = LogManager.getLogger(getClass()); + @Value("${upload.path}") + protected String uploadBasePath; + @Autowired protected T service; @@ -97,15 +101,32 @@ public ResponseEntity findOne(@PathVariable("fileUuid") UUID fileUuid) { if (entity.isPresent()) { S file = entity.get(); - - LOG.info("Successfully got file with UUID {}", fileUuid); - final HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentType(MediaType.parseMediaType(file.getFileType())); responseHeaders.setContentDisposition(ContentDisposition.parse( String.format("inline; filename=\"%s\"", file.getFileName()))); - return new ResponseEntity<>(file.getFile(), responseHeaders, HttpStatus.OK); + LOG.info("Successfully got file with UUID {}", fileUuid); + if (file.getPath() == null) { + return new ResponseEntity<>(file.getFile(), responseHeaders, HttpStatus.OK); + } + + java.io.File dataFile = new java.io.File(uploadBasePath + file.getPath()); + if (dataFile.exists()) { + return new ResponseEntity<>(dataFile, responseHeaders, HttpStatus.OK); + } + + LOG.error("Could not find entity of type {} with UUID {}", + getGenericClassName(), fileUuid); + + throw new ResponseStatusException( + HttpStatus.NOT_FOUND, + messageSource.getMessage( + "BaseController.NOT_FOUND", + null, + LocaleContextHolder.getLocale() + ) + ); } else { LOG.error("Could not find entity of type {} with UUID {}", getGenericClassName(), fileUuid); @@ -194,6 +215,49 @@ public S add(MultipartFile uploadedFile) { } } + @PostMapping(value = "/uploadToFileSystem", consumes = "multipart/form-data") + @ResponseStatus(HttpStatus.CREATED) + public S addToFileSystem(MultipartFile uploadedFile) { + LOG.debug("Requested to upload a multipart-file"); + + try { + + service.isValidType(uploadedFile.getContentType()); + + S persistedFile = service.create(uploadedFile, true); + + LOG.info("Successfully uploaded file " + persistedFile.getFileName()); + + return persistedFile; + } catch (AccessDeniedException ade) { + LOG.info("Uploading entity of type {} is denied", getGenericClassName()); + + throw new ResponseStatusException( + HttpStatus.NOT_FOUND, + messageSource.getMessage( + "BaseController.NOT_FOUND", + null, + LocaleContextHolder.getLocale() + ), + ade + ); + } catch (ResponseStatusException rse) { + throw rse; + } catch (Exception e) { + LOG.error("Could not upload the file: " + e.getMessage()); + LOG.trace("Full stack trace: ", e); + + throw new ResponseStatusException( + HttpStatus.INTERNAL_SERVER_ERROR, + messageSource.getMessage( + "BaseController.INTERNAL_SERVER_ERROR", + null, + LocaleContextHolder.getLocale() + ) + ); + } + } + @DeleteMapping("/{fileUuid}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable("fileUuid") UUID fileUuid) { diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java index 7d0336979..681ba1b89 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/model/File.java @@ -67,4 +67,8 @@ public class File extends BaseEntity { @Column(length = Integer.MAX_VALUE) @Getter @Setter private byte[] file; + + @Column + @Getter @Setter + private String path; } diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java index f999dc401..1941b1197 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/BaseFileService.java @@ -20,6 +20,10 @@ import de.terrestris.shogun.lib.repository.BaseFileRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import java.util.Optional; import java.util.UUID; @@ -27,8 +31,12 @@ public abstract class BaseFileService & JpaSpecificationExecutor, S extends File> extends BaseService implements IBaseFileService { @PostAuthorize("hasRole('ROLE_ADMIN') or hasPermission(returnObject.orElse(null), 'READ')") - public Optional findOne(UUID fileUuid) { - return repository.findByFileUuid(fileUuid); + public Optional findOne(UUID fileUuid) { return repository.findByFileUuid(fileUuid); } + @PreAuthorize("hasRole('ROLE_ADMIN') or hasPermission(#entity, 'CREATE')") + @Transactional(isolation = Isolation.SERIALIZABLE) + public abstract S create(MultipartFile uploadFile, Boolean writeToSystem) throws Exception; + + } diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java index 5364f06e5..7eea6d8d1 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/FileService.java @@ -34,6 +34,12 @@ public class FileService extends BaseFileService { @Autowired private UploadProperties uploadProperties; + /** + * + * @param uploadFile + * @return + * @throws Exception + */ public File create(MultipartFile uploadFile) throws Exception { FileUtil.validateFile(uploadFile); @@ -51,6 +57,38 @@ public File create(MultipartFile uploadFile) throws Exception { return savedFile; } + /** + * + * @param uploadFile + * @param writeToSystem + * @return + * @throws Exception + */ + public File create(MultipartFile uploadFile, Boolean writeToSystem) throws Exception { + + if (writeToSystem) { + return this.create(uploadFile); + } + + FileUtil.validateFile(uploadFile); + File file = new File(); + // TODO: generate path + String path = ""; + file.setPath(path); + file.setFileType(uploadFile.getContentType()); + file.setFileName(uploadFile.getOriginalFilename()); + file.setActive(true); + + File savedFile = this.create(file); + + return savedFile; + } + + /** + * + * @param contentType + * @throws InvalidContentTypeException + */ public void isValidType(String contentType) throws InvalidContentTypeException { if (uploadProperties == null) { throw new InvalidContentTypeException("No properties for the upload found. " + @@ -75,5 +113,4 @@ public void isValidType(String contentType) throws InvalidContentTypeException { throw new InvalidContentTypeException("Unsupported content type for upload!"); } } - } diff --git a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java index 5fe603cc9..696c090d9 100644 --- a/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java +++ b/shogun-lib/src/main/java/de/terrestris/shogun/lib/service/ImageFileService.java @@ -89,4 +89,11 @@ public void isValidType(String contentType) throws InvalidContentTypeException { } } + @Override + public ImageFile create(MultipartFile uploadFile, Boolean writeToSystem) throws Exception { + if (writeToSystem) { + return this.create(uploadFile); + } + return null; + } }