From b3144e5601870a972f7580a14131b6bd75c4a38b Mon Sep 17 00:00:00 2001 From: dizda13 Date: Sun, 2 Apr 2017 22:03:18 +0200 Subject: [PATCH] Add user profile picture storage service --- config | 2 +- .../src/main/resources/application.properties | 2 +- .../src/main/resources/application.properties | 2 +- .../ba/steleks/SteleksServiceApplication.java | 3 + .../controller/ProfilePictureController.java | 64 ++++++++++++++ .../src/main/java/ba/steleks/model/User.java | 2 +- .../storage/FileSystemStorageService.java | 87 +++++++++++++++++++ .../ba/steleks/storage/StorageException.java | 12 +++ .../storage/StorageFileNotFoundException.java | 13 +++ .../ba/steleks/storage/StorageProperties.java | 21 +++++ .../ba/steleks/storage/StorageService.java | 23 +++++ .../src/main/resources/application.properties | 2 +- 12 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 users/src/main/java/ba/steleks/controller/ProfilePictureController.java create mode 100644 users/src/main/java/ba/steleks/storage/FileSystemStorageService.java create mode 100644 users/src/main/java/ba/steleks/storage/StorageException.java create mode 100644 users/src/main/java/ba/steleks/storage/StorageFileNotFoundException.java create mode 100644 users/src/main/java/ba/steleks/storage/StorageProperties.java create mode 100644 users/src/main/java/ba/steleks/storage/StorageService.java diff --git a/config b/config index c02e79c..ccf6fbb 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit c02e79c9aad0a236f1549e849233f9b9193a025f +Subproject commit ccf6fbb013c16dbda4ce5c76de706caed5ccde72 diff --git a/events/src/main/resources/application.properties b/events/src/main/resources/application.properties index 6693c6c..bfe3465 100644 --- a/events/src/main/resources/application.properties +++ b/events/src/main/resources/application.properties @@ -1,7 +1,7 @@ server.port = 9020 spring.datasource.url = jdbc:mysql://localhost:3306/events spring.datasource.username = root -spring.datasource.password = root +spring.datasource.password = skorpion spring.jpa.generate-ddl=true user.password=dizda # diff --git a/teams/src/main/resources/application.properties b/teams/src/main/resources/application.properties index 0b71ced..c849d32 100644 --- a/teams/src/main/resources/application.properties +++ b/teams/src/main/resources/application.properties @@ -1,6 +1,6 @@ server.port=9010 spring.datasource.url = jdbc:mysql://localhost:3306/teams spring.datasource.username = root -spring.datasource.password = root +spring.datasource.password = skorpion spring.jpa.generate-ddl=true diff --git a/users/src/main/java/ba/steleks/SteleksServiceApplication.java b/users/src/main/java/ba/steleks/SteleksServiceApplication.java index 5b1ec13..2433e16 100644 --- a/users/src/main/java/ba/steleks/SteleksServiceApplication.java +++ b/users/src/main/java/ba/steleks/SteleksServiceApplication.java @@ -1,9 +1,12 @@ package ba.steleks; +import ba.steleks.storage.StorageProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; @SpringBootApplication +@EnableConfigurationProperties(StorageProperties.class) public class SteleksServiceApplication { public static void main(String[] args) { diff --git a/users/src/main/java/ba/steleks/controller/ProfilePictureController.java b/users/src/main/java/ba/steleks/controller/ProfilePictureController.java new file mode 100644 index 0000000..fadc321 --- /dev/null +++ b/users/src/main/java/ba/steleks/controller/ProfilePictureController.java @@ -0,0 +1,64 @@ +package ba.steleks.controller; + +import ba.steleks.model.User; +import ba.steleks.repository.UsersJpaRepository; +import ba.steleks.storage.StorageFileNotFoundException; +import ba.steleks.storage.StorageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.RepositoryRestController; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.core.io.Resource; +import java.util.Date; + +/** + * Created by admin on 02/04/2017. + */ +@RestController +public class ProfilePictureController { + + private final StorageService storageService; + private final UsersJpaRepository repository; + + @Autowired + public ProfilePictureController(StorageService storageService, UsersJpaRepository repository) { + this.storageService = storageService; + this.repository = repository; + } + + @GetMapping("/profilePictures/{filename:.+}") + @ResponseBody + public ResponseEntity serveFile(@PathVariable String filename) { + + Resource file = storageService.loadAsResource(filename); + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+file.getFilename()+"\"") + .body(file); + } + + @PostMapping("/users/{userId}/profilePicture") + public String handleFileUpload(@PathVariable Long userId, @RequestParam("file") MultipartFile file, + RedirectAttributes redirectAttributes) { + + String[] names = file.getOriginalFilename().split("\\."); + String dest = String.valueOf(userId + "_" + new Date().getTime()) + "." + names[names.length - 1]; + storageService.store(file, dest); + redirectAttributes.addFlashAttribute("message", + "You successfully uploaded " + file.getOriginalFilename() + "!"); + + User user = repository.findOne(userId); + user.setProfilePictureUrl("http://localhost:8090/profilePictures/" + dest); + repository.save(user); + + return "redirect:/"; + } + + @ExceptionHandler(StorageFileNotFoundException.class) + public ResponseEntity handleStorageFileNotFound(StorageFileNotFoundException exc) { + return ResponseEntity.notFound().build(); + } +} diff --git a/users/src/main/java/ba/steleks/model/User.java b/users/src/main/java/ba/steleks/model/User.java index 909683d..a5094ef 100644 --- a/users/src/main/java/ba/steleks/model/User.java +++ b/users/src/main/java/ba/steleks/model/User.java @@ -24,7 +24,7 @@ public class User { private String firstName; @NotNull private String lastName; - @Column(updatable = false, insertable = false, columnDefinition="DATETIME default NOW()") + @NotNull private Timestamp registrationDate; @NotNull private String email; diff --git a/users/src/main/java/ba/steleks/storage/FileSystemStorageService.java b/users/src/main/java/ba/steleks/storage/FileSystemStorageService.java new file mode 100644 index 0000000..4906eac --- /dev/null +++ b/users/src/main/java/ba/steleks/storage/FileSystemStorageService.java @@ -0,0 +1,87 @@ +package ba.steleks.storage; + +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.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +@Service +public class FileSystemStorageService implements StorageService { + + private final Path rootLocation; + + @Autowired + public FileSystemStorageService(StorageProperties properties) { + this.rootLocation = Paths.get(properties.getLocation()); + } + + @Override + public void store(MultipartFile file, String dest) { + try { + if (file.isEmpty()) { + throw new StorageException("Failed to store empty file " + file.getOriginalFilename()); + } + Files.copy(file.getInputStream(), + this.rootLocation.resolve(dest)); + } catch (IOException e) { + throw new StorageException("Failed to store file " + file.getOriginalFilename(), e); + } + } + + @Override + public Stream loadAll() { + try { + return Files.walk(this.rootLocation, 1) + .filter(path -> !path.equals(this.rootLocation)) + .map(path -> this.rootLocation.relativize(path)); + } 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 StorageFileNotFoundException("Could not read file: " + filename); + + } + } catch (MalformedURLException e) { + throw new StorageFileNotFoundException("Could not read file: " + filename, e); + } + } + + @Override + public void deleteAll() { + FileSystemUtils.deleteRecursively(rootLocation.toFile()); + } + + @Override + public void init() { + try { + Files.createDirectory(rootLocation); + } catch (IOException e) { + throw new StorageException("Could not initialize storage", e); + } + } +} \ No newline at end of file diff --git a/users/src/main/java/ba/steleks/storage/StorageException.java b/users/src/main/java/ba/steleks/storage/StorageException.java new file mode 100644 index 0000000..013fdb5 --- /dev/null +++ b/users/src/main/java/ba/steleks/storage/StorageException.java @@ -0,0 +1,12 @@ +package ba.steleks.storage; + +public class StorageException extends RuntimeException { + + public StorageException(String message) { + super(message); + } + + public StorageException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/users/src/main/java/ba/steleks/storage/StorageFileNotFoundException.java b/users/src/main/java/ba/steleks/storage/StorageFileNotFoundException.java new file mode 100644 index 0000000..2222d76 --- /dev/null +++ b/users/src/main/java/ba/steleks/storage/StorageFileNotFoundException.java @@ -0,0 +1,13 @@ +package ba.steleks.storage; + + +public class StorageFileNotFoundException extends StorageException { + + public StorageFileNotFoundException(String message) { + super(message); + } + + public StorageFileNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/users/src/main/java/ba/steleks/storage/StorageProperties.java b/users/src/main/java/ba/steleks/storage/StorageProperties.java new file mode 100644 index 0000000..eb6af08 --- /dev/null +++ b/users/src/main/java/ba/steleks/storage/StorageProperties.java @@ -0,0 +1,21 @@ +package ba.steleks.storage; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("storage") +public class StorageProperties { + + /** + * Folder location for storing files + */ + private String location = "/Users/admin"; + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + +} \ No newline at end of file diff --git a/users/src/main/java/ba/steleks/storage/StorageService.java b/users/src/main/java/ba/steleks/storage/StorageService.java new file mode 100644 index 0000000..c876efa --- /dev/null +++ b/users/src/main/java/ba/steleks/storage/StorageService.java @@ -0,0 +1,23 @@ +package ba.steleks.storage; + +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(); + + void store(MultipartFile file, String dest); + + Stream loadAll(); + + Path load(String filename); + + Resource loadAsResource(String filename); + + void deleteAll(); + +} \ No newline at end of file diff --git a/users/src/main/resources/application.properties b/users/src/main/resources/application.properties index 3ac2dce..e687b7f 100644 --- a/users/src/main/resources/application.properties +++ b/users/src/main/resources/application.properties @@ -1,6 +1,6 @@ server.port = 8090 spring.datasource.url = jdbc:mysql://localhost:3306/users spring.datasource.username = root -spring.datasource.password = root +spring.datasource.password = skorpion spring.jpa.generate-ddl=true