Add file upload routes.
This commit is contained in:
@@ -10,13 +10,21 @@ import org.codiki.core.entities.dto.PasswordWrapperDTO;
|
|||||||
import org.codiki.core.entities.dto.UserDTO;
|
import org.codiki.core.entities.dto.UserDTO;
|
||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.security.TokenService;
|
import org.codiki.core.security.TokenService;
|
||||||
|
import org.codiki.core.utils.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
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.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/account")
|
@RequestMapping("/api/account")
|
||||||
@@ -82,4 +90,24 @@ public class AccountController {
|
|||||||
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/uploadAvatar")
|
||||||
|
public ResponseEntity<String> uploadAvatar(@RequestParam("file") MultipartFile pFile) {
|
||||||
|
String result;
|
||||||
|
try {
|
||||||
|
result = accountService.uploadFile(pFile);
|
||||||
|
return ResponseEntity.status(HttpStatus.OK).body(result);
|
||||||
|
} catch(final Exception pEx) {
|
||||||
|
result = StringUtils.concat("Fail to upload ", pFile.getOriginalFilename() + ".");
|
||||||
|
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/loadAvatar/{avatarFileName}")
|
||||||
|
public ResponseEntity<Resource> loadAvatar(@PathVariable("avatarFileName") final String pAvatarFileName) {
|
||||||
|
final Resource avatarFile = accountService.loadAvatar(pAvatarFileName);
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, StringUtils.concat("attachment; filename=\"", avatarFile.getFilename(), "\""))
|
||||||
|
.body(avatarFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,25 @@ import org.codiki.core.entities.dto.UserDTO;
|
|||||||
import org.codiki.core.entities.persistence.User;
|
import org.codiki.core.entities.persistence.User;
|
||||||
import org.codiki.core.repositories.UserRepository;
|
import org.codiki.core.repositories.UserRepository;
|
||||||
import org.codiki.core.security.TokenService;
|
import org.codiki.core.security.TokenService;
|
||||||
|
import org.codiki.core.services.FileUploadService;
|
||||||
import org.codiki.core.utils.StringUtils;
|
import org.codiki.core.utils.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AccountService {
|
public class AccountService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private TokenService tokenService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FileUploadService fileUploadService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the user credentials and generate him a token if they are correct.
|
* Check the user credentials and generate him a token if they are correct.
|
||||||
*
|
*
|
||||||
@@ -68,4 +74,12 @@ public class AccountService {
|
|||||||
"Le mot de passe saisi ne correspond pas au votre.");
|
"Le mot de passe saisi ne correspond pas au votre.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String uploadFile(final MultipartFile pFile) {
|
||||||
|
return fileUploadService.uploadProfileImage(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource loadAvatar(final String pAvatarFileName) {
|
||||||
|
return fileUploadService.loadAvatar(pAvatarFileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/main/java/org/codiki/core/constant/FileEnum.java
Executable file
20
src/main/java/org/codiki/core/constant/FileEnum.java
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
package org.codiki.core.constant;
|
||||||
|
|
||||||
|
public enum FileEnum {
|
||||||
|
/** Folder in where pictures will be uploaded. */
|
||||||
|
FOLDER_UPLOAD("/opt/codiki/pictures/tmp/"),
|
||||||
|
/** Folder in where profile pictures will be stored. */
|
||||||
|
FOLDER_PROFILE_IMAGES("/opt/codiki/pictures/profiles/"),
|
||||||
|
/** Folder in where images will be stored. */
|
||||||
|
FOLDER_IMAGE("/opt/codiki/pictures/posts/");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private FileEnum(final String pValue) {
|
||||||
|
value = pValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String val() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/main/java/org/codiki/core/security/CorsFilter.java
Executable file
44
src/main/java/org/codiki/core/security/CorsFilter.java
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
package org.codiki.core.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.FilterConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public class CorsFilter implements Filter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||||
|
httpResponse.addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
httpResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, HEAD");
|
||||||
|
httpResponse.addHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
|
||||||
|
httpResponse.addHeader("Access-Control-Expose-Headers", "Access-Control-Allow-Origin, Access-Control-Allow-Credentials");
|
||||||
|
httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
|
||||||
|
httpResponse.addIntHeader("Access-Control-Max-Age", 10);
|
||||||
|
chain.doFilter(request, httpResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
61
src/main/java/org/codiki/core/services/FileUploadService.java
Executable file
61
src/main/java/org/codiki/core/services/FileUploadService.java
Executable file
@@ -0,0 +1,61 @@
|
|||||||
|
package org.codiki.core.services;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.RandomStringUtils;
|
||||||
|
import org.codiki.core.constant.FileEnum;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.UrlResource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class FileUploadService {
|
||||||
|
/** Length of uploaded file names. */
|
||||||
|
private static final int DESTINATION_IMAGE_NAME_LENGTH = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the destination file name, which is a random string with 30 char
|
||||||
|
* length.
|
||||||
|
*
|
||||||
|
* @return The file name.
|
||||||
|
*/
|
||||||
|
private String buildDestinationFileName() {
|
||||||
|
return RandomStringUtils.randomAlphanumeric(DESTINATION_IMAGE_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String uploadProfileImage(final MultipartFile pFile) {
|
||||||
|
return uploadFile(pFile, FileEnum.FOLDER_PROFILE_IMAGES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String uploadFile(final MultipartFile pFile, final FileEnum pPath) {
|
||||||
|
String result;
|
||||||
|
try {
|
||||||
|
result = buildDestinationFileName();
|
||||||
|
Files.copy(pFile.getInputStream(), Paths.get(pPath.val()).resolve(result));
|
||||||
|
return result;
|
||||||
|
} catch (final Exception pEx) {
|
||||||
|
// TODO : Refactor exception
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource loadAvatar(final String pAvatarFileName) {
|
||||||
|
try {
|
||||||
|
Path avatarFile = Paths.get(FileEnum.FOLDER_PROFILE_IMAGES.val()).resolve(pAvatarFileName);
|
||||||
|
Resource avatarResource = new UrlResource(avatarFile.toUri());
|
||||||
|
if(avatarResource.exists() || avatarResource.isReadable()) {
|
||||||
|
return avatarResource;
|
||||||
|
} else {
|
||||||
|
// TODO : Refactor exception
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
} catch(final MalformedURLException pEx) {
|
||||||
|
// TODO : Refactor exception
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,4 +12,6 @@ spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
|
|||||||
|
|
||||||
logging.level.org.hibernate=DEBUG
|
logging.level.org.hibernate=DEBUG
|
||||||
|
|
||||||
|
spring.servlet.multipart.max-file-size=104857600
|
||||||
|
|
||||||
cors.enabled=false
|
cors.enabled=false
|
||||||
Reference in New Issue
Block a user