원모싸이버스쿨 앱 가운데 이미지를 업로드하고 업로드한 이미지를 다운로드 하거나 보여지게 하고싶다면 바로바로
원모 싸이버 이미지 서버를 이용하면 됩니다.
https://github.com/yonmoyonmo/wcs-image-server
이 분의 설명글을 참조했습니다.(거의 똑 같음)
원모 싸이버 이미지 서버
간단 설명
기능 :
- 요청에 따라 알맞게 이미지 파일을 저장 후 경로를 제공
- 저장된 이미지를 브라우저 상에서 볼 수 있도록 리소스 제공
두 가지 심플하고 딱 필요한 기능만 있습니다.
짱이죠?
코드도 엄청 간단합니다.
코드 살피기
application.properties
... 별 다를 것 없는 JPA, DATA SOURCE 속성 생략 ...
## multipart 관련 속성
# 멀티파트 업로드 가능하게 하긔
spring.servlet.multipart.enabled=true
# 파일이 디스크에 쓰인 후의 쓰레쉬홀드
spring.servlet.multipart.file-size-threshold=2KB
# 최대 파일 사이즈
spring.servlet.multipart.max-file-size=200MB
# 최대 리퀘스트 사이즈
spring.servlet.multipart.max-request-size=215MB
# 파일 저장될 디렉토리
# 프로퍼티 클래스 만들어서 쓰면 댑니다
file.upload-dir=./images
요런 속성이 필요합니다.
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
업로드 디렉토리 정보를 application.properties에서 가져와서 쓸 수 있도록 합니다.
@SpringBootApplication
@EnableConfigurationProperties(FileStorageProperties.class)
public class ImageServerApplication {
public static void main(String[] args) {
SpringApplication.run(ImageServerApplication.class, args);
}
메인 어플리케이션 클래스에 @EnableConfigurationProperties 써 주어야지 프로퍼티 클래스를 쓸 수 있습니다.
다음에는 Request와 Response를 처리하기 위한 Controller 클래스를 만들고 파일을 저장하거나 가져오는 로직을 담당하는 Service 클래스를 만들고 컨트롤러와 서비스 사이에 쓸 DTO와 Exception들을 만들어 주면됩니다.
FileController
import com.wonmocyberschool.imageserver.payload.Response;
import com.wonmocyberschool.imageserver.service.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/wcs/image")
public class FileController {
private static final Logger logger = LoggerFactory.getLogger(FileController.class);
private final StorageService storageService;
@Autowired
public FileController(StorageService storageService){
this.storageService = storageService;
}
@PostMapping("/upload")
public ResponseEntity<Response> uploadImage(@RequestParam("file") MultipartFile file,
@RequestParam("userName") String userName) throws IOException {
Response res = new Response();
try{
String result = storageService.saveFile(file, userName);
res.setImageLocation("/"+userName+"/"+result);
res.setMessage("done");
res.setSuccess(true);
return new ResponseEntity<Response>(res, HttpStatus.OK);
}catch (Exception e){
res.setMessage("failed");
res.setSuccess(false);
return new ResponseEntity<Response>(res, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/post/upload")
public ResponseEntity<Response> postImageUpload(@RequestParam("files") MultipartFile[] files,
@RequestParam("postName")String postName) {
Response res = new Response();
List<String> results = new ArrayList<>();
List<String> imageLocations = new ArrayList<>();
try{
results = storageService.saveFiles(files, postName);
for(String result : results){
imageLocations.add("/"+postName+"/"+result);
}
res.setImageLocations(imageLocations);
res.setMessage("done");
res.setSuccess(true);
return new ResponseEntity<Response>(res, HttpStatus.OK);
}catch (Exception e){
res.setMessage("failed");
res.setSuccess(false);
return new ResponseEntity<Response>(res, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/display/{userName}/{fileName:.+}")
public ResponseEntity<Resource> displayImage(@PathVariable String fileName,
@PathVariable String userName,
HttpServletRequest request) {
// Load file as Resource
Resource resource = storageService.loadFileAsResource(userName, fileName);
// Try to determine file's content type
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException ex) {
logger.info("Could not determine file type.");
}
// Fallback to the default content type if type could not be determined
if(contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
}
StorageService
package com.wonmocyberschool.imageserver.service;
import com.wonmocyberschool.imageserver.config.FileStorageProperties;
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.StringUtils;
import org.springframework.web.multipart.MultipartFile;
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.ArrayList;
import java.util.List;
import java.util.Random;
@Service
public class StorageService {
private String uploadPath;
@Autowired
public StorageService(FileStorageProperties fileStorageProperties){
this.uploadPath = fileStorageProperties.getUploadDir();
}
private String getRandomStr(){
int leftLimit = 97; // letter 'a'
int rightLimit = 122; // letter 'z'
int targetStringLength = 10;
Random random = new Random();
String generatedString = random.ints(leftLimit, rightLimit + 1)
.limit(targetStringLength)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
System.out.println("random : " + generatedString);
return generatedString;
}
public List<String> saveFiles(MultipartFile[] files, String postName) throws IOException {
String randomStr = getRandomStr();
List<String> fileNames = new ArrayList<>();
for(MultipartFile file : files) {
fileNames.add(randomStr + StringUtils.cleanPath(file.getOriginalFilename()));
}
Path uploadPath = Paths.get(this.uploadPath+"/"+postName);
if(!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
System.out.println("make dir : " + uploadPath.toString());
}
for(int i =0; i< files.length; i++) {
try (InputStream inputStream = files[i].getInputStream()) {
Path filePath = uploadPath.resolve(fileNames.get(i));
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ioe) {
throw new IOException("Could not save image file: " + fileNames.get(i), ioe);
}
}
return fileNames;
}
public String saveFile(MultipartFile file, String userName) throws IOException {
String randomStr = getRandomStr();
String fileName = randomStr + StringUtils.cleanPath(file.getOriginalFilename());
Path uploadPath = Paths.get(this.uploadPath+"/"+userName);
if(!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
try (InputStream inputStream = file.getInputStream()) {
Path filePath = uploadPath.resolve(fileName);
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ioe) {
throw new IOException("Could not save image file: " + fileName, ioe);
}
}
public Resource loadFileAsResource(String userName, String fileName) {
Path uploadPath = Paths.get(this.uploadPath+"/"+userName);
try {
Path filePath = uploadPath.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if(resource.exists()) {
return resource;
} else {
throw new MyFileNotFoundException("File not found " + fileName);
}
} catch (MalformedURLException ex) {
throw new MyFileNotFoundException("File not found " + fileName, ex);
}
}
}
Response
package com.wonmocyberschool.imageserver.payload;
import java.util.List;
public class Response {
private String message;
private String imageLocation;
private List<String> imageLocations;
private boolean success;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getImageLocation() {
return imageLocation;
}
public void setImageLocation(String imageLocation) {
this.imageLocation = imageLocation;
}
public List<String> getImageLocations() {
return imageLocations;
}
public void setImageLocations(List<String> imageLocations) {
this.imageLocations = imageLocations;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
위의 코드 외에 생략한 코드들은 Cors 설정, 예외 클래스 등입니다. 깃허브에서 확인할 수 있습니닷.
요로케 하나 만들어 놓으면 든든- 합니다. 단점은 클러스터링을 하려 하면 프로세스별로 저장한 파일이 달라지므로 파일 동기화를 위한 작업을 해 줘야 한다는 점!
하지만 간단한 것이 주는 장점이 참 크지요?!
이미지 좀 저장하자고 S3쓰긴 돈아깝자나~~!
끝
'프로그래밍 > Spring Boot' 카테고리의 다른 글
[ Spring boot + JPA + MySQL ] 원모 싸이버 스쿨 블로그 API 서버 (0) | 2021.07.16 |
---|---|
[Spring Boot Security OAuth 2.0] 원모 싸이버 스쿨 인증서버 (0) | 2021.07.15 |
스프링 시큐리티(Spring Security) 간략히 정리 (0) | 2021.05.27 |
댓글