yahayaha

27-1. 파일 업로드(썸네일 이미지) 본문

spring/프로젝트

27-1. 파일 업로드(썸네일 이미지)

yaha 2024. 2. 17. 17:53

지금까지 완료된 작업은 이미지 경로에 대한 처리, 그리고 중복 이름에 대한 처리를 완료한 상태.
 
이제 남은건 이게 이미지 파일인지 일반 파일인지 구분을 해야함.
 
이미지 파일 같은 경우에는 화면에 보여지는 이미지(썸네일 섬네일)를 생성하는 작업을 해야함.

섬네일이지만 그냥 썸네일이 입에 착 달라붙으니 썸네일이라 하겠음.

 
썸네일을 제작하는 방법으로 여러가지가 있는데  JDK1.4부터는 ImageIO를 제공하기 때문에 이를 이용해서 원본 이미지를 줄일 수도 있고, 별도의 라이브러리를 이용하는 방식도 있음.
 
JDK에 포함된 API를 이용하는 방식보다 별도의 라이브러리를 사용하는 경우가 많음. 그 이유는 이미지를 축소했을 때의 크기나 해상도를 직접 조절한느 작업을 줄이기 위함.
 
그럼 썸네일 이미지를 작업하기위해 maven 에서 thumbnailator 라이브러리를 pom.xml에 추가.
 

<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.8</version>
</dependency>

 
이제 검사를 통해서 업로드되는 파일의 확장자를 검사하긴 하지만, Ajax로 사용하는 호출은 반드시 브라우저만을 통해서 들어오는 것이 아니므로 확인할 필요가 있음.
 
서버에 업로드된 파일은 시간이 걸려도 파일 자체가 이미지인지를 정확히 체크한 뒤 저장하는게 좋음.
 
특정 파일이 이미지 타입인지 검사하는 메서드를 추가하고, 이미지 타입이면 썸네일을 생성하는 코드를 수정.
 

<!-- 특정한 파일이 이미지 타입인지를 검사하는 메서드 -->
private boolean checkImageType(File file) {
    try {
        // 파일의 ContentType을 확인하여 이미지인지 여부를 판단
        String contentType = Files.probeContentType(file.toPath());
        // ContentType의 startsWith함수가 "image"로 시작하면 이미지 파일로 판단.
        return contentType.startsWith("image");
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

------------------------------------------------

@PostMapping("/uploadAjaxAction")
public void uploadAjaxPost(MultipartFile[] uploadFile) {
    log.info("update ajax post...");

    //파일을 업로드할 기본 폴더 경로
    String uploadFolder = "C:\\upload";

    //폴더 만드는 부분 -------
    File uploadPath = new File(uploadFolder, getForlder());
    log.info("업로드 경로 : " + uploadPath);

    // 업로드 경로가 존재하지 않을 경우 폴더 생성
    if(uploadPath.exists() == false) {
        uploadPath.mkdirs();
    }

    // 각 파일에 대해 업로드 처리 수행
    for(MultipartFile multipartFile : uploadFile) {
        log.info("-------------");
        log.info("Upload File Name : " + multipartFile.getOriginalFilename());
        log.info("Upload File Name : " + multipartFile.getSize());

        // 업로드된 파일의 이름 가져오기
        String uploadFileName = multipartFile.getOriginalFilename();

         // Internet Explorer의 경우 파일 경로 제거
        uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
        log.info("only file name : " + uploadFileName);

        //중복 방지 UUID
        UUID uuid = UUID.randomUUID();

        //파일 이름에 하이픈 추가해서 중복 파일 이름 방지.
        uploadFileName = uuid.toString() + "_" + uploadFileName;


        try {
            //년 월 일에 생성된 파일저장.
            File saveFile = new File(uploadPath, uploadFileName);

            // 업로드된 파일을 저장 경로로 이동
            multipartFile.transferTo(saveFile);

			<!-- 썸네일 생성 -->
            // 파일이 이미지 타입인지 체크해서 썸네일을 만듬.
            if(checkImageType(saveFile)) {

                // 썸네일 생성부분
                FileOutputStream thumbnail = 
                new FileOutputStream(new File(uploadPath, "s_" + uploadFileName));

                Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 100, 100);

                thumbnail.close();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }



} // end uploadAjaxAction

 
Thumbnailator는 inputStream과 java.io.File 객체를 이용해서 파일을 생성할 수 있음.
 
뒤에 사이즈에 대한 부분은 파라미터로 width와 height를 지정.
 
그리고 이미지 파일을 업로드하면 원본 파일은 그대로 저장되고, 파일 이름이 s_로 시작하는 썸네일 파일이 생성됨.
 

 
Ajax를 이용해서 파일을 업로드 했지만, 아직 브라우저 쪽에 아무런 데이터도 전달하지 않은 데이터여서 어떠한 피드백도 받을 수 없음.
 
서버에서 Ajax의 결과로 전달해야 하는 데이터는 업로드된 파일의 경로가 포함된 파일 이름.
썸네일의 경우 's_'로 시작한다는 규칙만 알고 있으면 유용하게 사용가능.
 
브라우저로 전송해야 하는 데이터는 세가지 정보를 포함하도록 설계를 해야함.
1. 업로드된 파일의 이름과 원본 파일의 이름.
2. 파일이 저장된 경로.
3. 업로드된 파일이 이미지인지 아닌지에 대한 정보.
 
여러가지 방법이 있지만
 
별도의 객체를 생성해서 처리하는 방법으로 구성을 진행.
 
먼저 pom.xml에 jackson-databind 관련 라이브러리를 추가.
 

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.6</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.6</version>
</dependency>

 
첨부파일을 저장하는 DTO 클래스를 생성.
 

@Data
public class AttachFileDTO {
    private String fileName;
    private String uploadPath;
    private String uuid;
    private boolean image;
}

 
AttachFileDTO 클래스는 원본 파일의 이름, 경로, UUID값, 이미지 여부 정보를 하나로 묶어서 전달하는 용도.
 
그에 따라서 uploadController는 AttachFileDTO의 리스트를 반환하는 구조로 변경해야함.
 

@PostMapping(value = "/uploadAjaxAction", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<List<AttachFileDTO>>
uploadAjaxPost(MultipartFile[] uploadFile) {
    log.info("update ajax post...");

    // 업로드된 파일 정보를 담을 리스트 생성
    List<AttachFileDTO> list = new ArrayList<>();

    //파일을 업로드할 기본 폴더 경로
    String uploadFolder = "C:\\upload";

    //폴더 만드는 부분 -------
    String uploadFolderPath = getForlder();
    File uploadPath = new File(uploadFolder, uploadFolderPath);
    log.info("업로드 경로 : " + uploadPath);

    // 업로드 경로가 존재하지 않을 경우 폴더 생성
    if(uploadPath.exists() == false) {
        uploadPath.mkdirs();
    }

    // 각 파일에 대해 업로드 처리 수행
    for(MultipartFile multipartFile : uploadFile) {

        AttachFileDTO attachDTO = new AttachFileDTO();

        // 업로드된 파일의 이름 가져오기
        String uploadFileName = multipartFile.getOriginalFilename();

         // Internet Explorer의 경우 파일 경로 제거
        uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
        log.info("only file name : " + uploadFileName);

        //파일 이름 설정.
        attachDTO.setFileName(uploadFileName);

        //중복 방지 UUID
        UUID uuid = UUID.randomUUID();

        //파일 이름에 하이픈 추가해서 중복 파일 이름 방지.
        uploadFileName = uuid.toString() + "_" + uploadFileName;

        try {
            //년 월 일에 생성된 파일저장.
            File saveFile = new File(uploadPath, uploadFileName);

            // 업로드된 파일을 저장 경로로 이동
            multipartFile.transferTo(saveFile);

            // AttachFileDTO에 파일 정보 설정
            attachDTO.setUuid(uuid.toString());
            attachDTO.setUploadPath(uploadFolderPath);

            // 파일이 이미지 타입인지 체크해서 썸네일을 만듬.
            if(checkImageType(saveFile)) {

                // 이미지 파일인 경우에만 이미지 플래그를 true로 설정
                attachDTO.setImage(true);

                // 썸네일 생성부분
                FileOutputStream thumbnail = 
                new FileOutputStream(new File(uploadPath, "s_" + uploadFileName));

                Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 100, 100);

                thumbnail.close();
            }

            // 리스트에 AttachFileDTO 추가
            list.add(attachDTO);

        } catch (Exception e) {
            log.error(e.getMessage());
        }
    } // end for
    // ResponseEntity를 사용하여 리스트와 HTTP 상태코드 반환
    return new ResponseEntity<>(list, HttpStatus.OK);

} // end uploadAjaxAction

 
기존과 달리 ResponseEntity<List<AttachFileDTO>>를 반화하는 형태로 수정.
 
JSON 데이터를 반환하도록 변경해야함.
 
내부에서는 각 파일에 맞게 AttachFileDTO를 생성해서 전달하는 구조로 변경.
 
그럼 이제 브라우저에서 Ajax 처리를 해줘야함. 
 
uploadAjax.jsp에서는 결과 데이터를 Javascript를 이용해서 반환된 정보를 처리하도록 수정을 진행.

$.ajax({
    url: '/uploadAjaxAction',
    processData: false,
    contentType: false,
    data: formData,
    type: 'POST',
    dataType : 'json',
    success: function(result){
        console.log(result);
    }
}); //$.ajax

 
호출했을 때의 결과 타입은 json으로 변경 결과를 console.log에 찍도록 수정 후 파일을 업로드하고 console.log를 확인.
 

 
그러면 첨부파일을 업로드 하고 브라우저에 결과를 확인할 수 있음.