Skip to content

Commit

Permalink
Allows saving of files to filesystem
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiVolland committed May 3, 2021
1 parent ae66675 commit 2cf6f98
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE shogun.files ADD COLUMN IF NOT EXISTS path text;
ALTER TABLE shogun.imagefiles ADD COLUMN IF NOT EXISTS path text;
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,4 +32,8 @@ public class UploadProperties {
@NestedConfigurationProperty
private ImageFileUploadProperties image;

private String path;

private String maxSize;

}
2 changes: 2 additions & 0 deletions shogun-config/src/main/resources/application-base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,5 @@ upload:
- image/png
- image/svg+xml
- image/tiff
path: /data
maxSize: 500M
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,6 +39,9 @@ public abstract class BaseFileController<T extends BaseFileService<?, S>, S exte

protected final Logger LOG = LogManager.getLogger(getClass());

@Value("${upload.path}")
protected String uploadBasePath;

@Autowired
protected T service;

Expand Down Expand Up @@ -97,28 +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);
} else {
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()
)
);
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()
)
);
} catch (AccessDeniedException ade) {
LOG.info("Access to entity of type {} with UUID {} is denied",
getGenericClassName(), fileUuid);
Expand Down Expand Up @@ -194,6 +202,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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,23 @@
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;

public abstract class BaseFileService<T extends BaseFileRepository<S, Long> & JpaSpecificationExecutor<S>, S extends File> extends BaseService<T, S> implements IBaseFileService<T, S> {

@PostAuthorize("hasRole('ROLE_ADMIN') or hasPermission(returnObject.orElse(null), 'READ')")
public Optional<S> findOne(UUID fileUuid) {
return repository.findByFileUuid(fileUuid);
public Optional<S> 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;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,34 @@
import de.terrestris.shogun.lib.repository.FileRepository;
import de.terrestris.shogun.lib.util.FileUtil;
import de.terrestris.shogun.properties.UploadProperties;
import org.apache.commons.io.IOUtils;
import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;

@Service
public class FileService extends BaseFileService<FileRepository, File> {

@Autowired
private UploadProperties uploadProperties;

/**
* Creates a new File entity in the Database.
* The file content is stored as bytearray in the DB.
*
* @param uploadFile
* @return
* @throws Exception
*/
public File create(MultipartFile uploadFile) throws Exception {

FileUtil.validateFile(uploadFile);
Expand All @@ -51,6 +65,63 @@ public File create(MultipartFile uploadFile) throws Exception {
return savedFile;
}

/**
* Creates a new File entity in the Database.
* The file is stored as file on the .
*
* @param uploadFile
* @param writeToSystem
* @return
* @throws Exception
*/
public File create(MultipartFile uploadFile, Boolean writeToSystem) throws Exception {

if (writeToSystem) {
return this.create(uploadFile);
}

String uploadBasePath = uploadProperties.getPath();
String filename = uploadFile.getOriginalFilename();

FileUtil.validateFile(uploadFile);
File file = new File();
file.setFileType(uploadFile.getContentType());
file.setFileName(filename);
file.setActive(true);

File savedFile = this.create(file);

UUID fileUuid = savedFile.getFileUuid();

// Setup path and directory
String path = fileUuid + "/" + filename;
java.io.File fileDirectory = new java.io.File(uploadBasePath + "/" + fileUuid);
fileDirectory.mkdirs();

// Write multipart file data to target directory
byte[] fileByteArray = FileUtil.fileToByteArray(uploadFile);
java.io.File outFile = new java.io.File(fileDirectory, filename);
InputStream in = new ByteArrayInputStream(fileByteArray);

try (OutputStream out = new FileOutputStream(outFile)) {
IOUtils.copy(in, out);
} catch (Exception e) {
LOG.error("Error when saving file to disk: " + e.getMessage());
throw e;
}

// Update entity with saved File
savedFile.setPath(path);
this.repository.save(savedFile);

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. " +
Expand All @@ -75,5 +146,4 @@ public void isValidType(String contentType) throws InvalidContentTypeException {
throw new InvalidContentTypeException("Unsupported content type for upload!");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@
import de.terrestris.shogun.lib.util.FileUtil;
import de.terrestris.shogun.lib.util.ImageFileUtil;
import de.terrestris.shogun.properties.UploadProperties;
import org.apache.commons.io.IOUtils;
import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.multipart.MultipartFile;

import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;

@Service
public class ImageFileService extends BaseFileService<ImageFileRepository, ImageFile> {
Expand Down Expand Up @@ -89,4 +95,59 @@ public void isValidType(String contentType) throws InvalidContentTypeException {
}
}

@Override
public ImageFile create(MultipartFile uploadFile, Boolean writeToSystem) throws Exception {
if (writeToSystem) {
return this.create(uploadFile);
}

String uploadBasePath = uploadProperties.getPath();
String filename = uploadFile.getOriginalFilename();

FileUtil.validateFile(uploadFile);

byte[] fileByteArray = FileUtil.fileToByteArray(uploadFile);

ImageFile file = new ImageFile();
file.setFileType(uploadFile.getContentType());
file.setFileName(uploadFile.getOriginalFilename());
file.setActive(true);

Dimension imageDimensions = ImageFileUtil.getImageDimensions(uploadFile);
if (imageDimensions != null) {
int thumbnailSize = uploadProperties.getImage().getThumbnailSize();
file.setThumbnail(ImageFileUtil.getScaledImage(uploadFile, imageDimensions, thumbnailSize));
file.setWidth(imageDimensions.width);
file.setHeight(imageDimensions.height);
} else {
LOG.warn("Could not detect the dimensions of the image. Neither width, height " +
"nor the thumbnail can be set.");
}

ImageFile savedFile = this.create(file);
UUID fileUuid = savedFile.getFileUuid();

// Setup path and directory
String path = fileUuid + "/" + filename;
java.io.File fileDirectory = new java.io.File(uploadBasePath + "/" + fileUuid);
fileDirectory.mkdirs();

// Write multipart file data to target directory
java.io.File outFile = new java.io.File(fileDirectory, filename);
InputStream in = new ByteArrayInputStream(fileByteArray);

try (OutputStream out = new FileOutputStream(outFile)) {
IOUtils.copy(in, out);
} catch (Exception e) {
LOG.error("Error when saving file to disk: " + e.getMessage());
throw e;
}

// Update entity with saved File
savedFile.setPath(path);
this.repository.save(savedFile);


return savedFile;
}
}

0 comments on commit 2cf6f98

Please sign in to comment.