이전글 보기

https://aamoos.tistory.com/683

 

[Spring Jpa] 13. 게시판 만들기 - 게시판 조회수 개발

이전글 보기 https://aamoos.tistory.com/681 [Spring Jpa] 12. 게시판 만들기 - 글 상세, 수정하기 이전글보기 https://aamoos.tistory.com/680 [Spring Jpa] 11. 게시판 만들기 - 유효성 검증, @Valid 사용해보..

aamoos.tistory.com

 

목표

- 파일을 여러개 올릴수 있는 input을 추가한후 멀티파일 업로드 기능을 구현하려고 합니다. 이번장에서는 파일 업로드와, File 테이블에 업로드한 파일의 정보만 쌓는 부분만 개발을 해보겠습니다.

 

소스

write.html

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/default_layout}">

<head layout:fragment="css">
    <style>
         .fieldError {
             border-color: #bd2130;
         }

         .form-group p{
            color: red;
         }
    </style>
</head>

<div layout:fragment="content" class="content">
    <form th:action th:object="${boardDto}" method="post" enctype="multipart/form-data">
        <article>
            <div class="container" role="main">
                <div class="form-group">
                    <label for="title">제목</label>
                    <input type="text" class="form-control" id="title" name="title" placeholder="제목을 입력해 주세요" th:class="${#fields.hasErrors('title')}? 'form-control fieldError' : 'form-control'">
                    <p th:if="${#fields.hasErrors('title')}" th:errors="*{title}">Incorrect date</p>
                </div>
                <br>
                <div class="mb-3">
                    <label for="reg_id">작성자</label>
                    <input type="text" class="form-control" id="reg_id" name="regId"  value="관리자" readonly>
                </div>
                <br>
                <div class="mb-3">
                    <label for="content">내용</label>
                    <textarea class="form-control" rows="5" id="content" name="content" placeholder="내용을 입력해 주세요"></textarea>
                </div>
                <br>
                <div class="mb-3">
                    <label for="formFileMultiple" class="form-label">파일업로드</label>
                    <input class="form-control" type="file" id="formFileMultiple" name="multipartFile" multiple>
                </div>
                <br>
                <div>
                    <button type="submit" class="btn btn-sm btn-primary" id="btnSave">저장</button>
                    <button onclick="location.href='/'" type="button" class="btn btn-sm btn-primary" id="btnList">목록</button>
                </div>
            </div>
        </article>
    </form>
</div>
</html>

<script>
</script>
- 파일 업로드 영역 을 추가하였습니다

 

화면

 

BoardService.java

@Transactional
    public Long saveBoard(BoardDto boardDto) throws Exception {
        List<Member> memberList = memberRepository.findAll();
        Member member = memberList.get(0);
        Board board = null;

        //insert
        if(boardDto.getId() == null){
            board = boardDto.toEntity(member);
            boardRepository.save(board);
        }

        //update
        else{
            board = boardRepository.findById(boardDto.getId()).get();
            board.update(boardDto.getTitle(), boardDto.getContent());
        }

        //파일 저장
        fileService.saveFile(boardDto);

        return board.getId();
    }
- 기존 코드에서는 게시글 저장할때 파일을 저장하지 않았습니다. FileService를 하나 생성후 saveFile을 호출하도록 추가합니다.

 

FileService.java

package jpa.board.service;

import jpa.board.dto.BoardDto;
import jpa.board.dto.FileDto;
import jpa.board.repository.FileRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.InputStream;
import java.util.*;

/**
 * packageName    : jpa.board.service
 * fileName       : FileService
 * author         : 김재성
 * date           : 2022-08-05
 * description    :
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2022-08-05        김재성       최초 생성
 */

@Service
@RequiredArgsConstructor
@Slf4j
public class FileService {

    @Value("${upload.path}")
    private String uploadDir;

    private final FileRepository fileRepository;

    @Transactional
    public Map<String, Object> saveFile(BoardDto boardDto) throws Exception {
        List<MultipartFile> multipartFile = boardDto.getMultipartFile();

        //결과 Map
        Map<String, Object> result = new HashMap<String, Object>();

        //파일 시퀀스 리스트
        List<Long> fileIds = new ArrayList<Long>();

        try {
            if (multipartFile != null) {
                if (multipartFile.size() > 0 && !multipartFile.get(0).getOriginalFilename().equals("")) {
                    for (MultipartFile file1 : multipartFile) {
                        String originalFileName = file1.getOriginalFilename();    //오리지날 파일명
                        String extension = originalFileName.substring(originalFileName.lastIndexOf("."));    //파일 확장자
                        String savedFileName = UUID.randomUUID() + extension;    //저장될 파일 명

                        File targetFile = new File(uploadDir + savedFileName);

                        //초기값으로 fail 설정
                        result.put("result", "FAIL");

                        FileDto fileDto = FileDto.builder()
                                .originFileName(originalFileName)
                                .savedFileName(savedFileName)
                                .uploadDir(uploadDir)
                                .extension(extension)
                                .size(file1.getSize())
                                .contentType(file1.getContentType())
                                .build();
                        //파일 insert
                        Long fileId = insertFile(fileDto.toEntity());
                        log.info("fileId={}", fileId);

                        try {
                            InputStream fileStream = file1.getInputStream();
                            FileUtils.copyInputStreamToFile(fileStream, targetFile); //파일 저장
                            //배열에 담기
                            fileIds.add(fileId);
                            result.put("fileIdxs", fileIds.toString());
                            result.put("result", "OK");
                        } catch (Exception e) {
                            //파일삭제
                            FileUtils.deleteQuietly(targetFile);    //저장된 현재 파일 삭제
                            e.printStackTrace();
                            result.put("result", "FAIL");
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /** 파일 저장 db */
    @Transactional
    public Long insertFile(jpa.board.entity.File file) {
        return fileRepository.save(file).getId();
    }

}
- List<MultipartFile>로 file들을 받아서 for문으로 돌려서 해당 경로에 파일을 저장하고 db에 쌓는 방식으로 개발하였습니다.

 

File.java

package jpa.board.entity;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;

/**
 * packageName    : jpa.board.entity
 * fileName       : File
 * author         : 김재성
 * date           : 2022-08-05
 * description    :
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2022-08-05        김재성       최초 생성
 */
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@EntityListeners(AuditingEntityListener.class)
public class File {

    @Id
    @GeneratedValue
    @Column(name = "file_id")
    private Long id;                    //id

    @Column(nullable = false)
    private String originFileName;      //원본 파일명

    @Column(nullable = false)
    private String savedFileName;       //저장된 파일명

    private String uploadDir;           //경로명

    private String extension;           //확장자

    private Long size;                  //파일 사이즈

    private String contentType;         //ContentType

    @CreatedDate
    private LocalDateTime regDate;     //등록 날짜

    @Builder
    public File(Long id, String originFileName, String savedFileName
            , String uploadDir, String extension, Long size, String contentType){
        this.id = id;
        this.originFileName = originFileName;
        this.savedFileName = savedFileName;
        this.uploadDir = uploadDir;
        this.extension = extension;
        this.size = size;
        this.contentType = contentType;
    }
}

 

FileDto.java

package jpa.board.dto;

import jpa.board.entity.Board;
import jpa.board.entity.File;
import jpa.board.entity.Member;
import lombok.Builder;
import lombok.Data;

import javax.persistence.Column;

/**
 * packageName    : jpa.board.dto
 * fileName       : FileDto
 * author         : 김재성
 * date           : 2022-08-05
 * description    :
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2022-08-05        김재성       최초 생성
 */

@Data
public class FileDto {

    private Long id;                    //id

    private String originFileName;      //원본 파일명

    private String savedFileName;       //저장된 파일명

    private String uploadDir;           //경로명

    private String extension;           //확장자

    private Long size;                  //파일 사이즈

    private String contentType;         //ContentType

    public FileDto(){

    }

    @Builder
    public FileDto(Long id, String originFileName, String savedFileName, String uploadDir
    , String extension, Long size, String contentType){
        this.id = id;
        this.originFileName = originFileName;
        this.savedFileName = savedFileName;
        this.uploadDir = uploadDir;
        this.extension = extension;
        this.size = size;
        this.contentType = contentType;
    }

    public File toEntity(){
        return File.builder()
                .originFileName(originFileName)
                .savedFileName(savedFileName)
                .uploadDir(uploadDir)
                .extension(extension)
                .size(size)
                .contentType(contentType)
                .build();
    }

}

 

application.yml

spring: #띄어쓰기 없음
  datasource: #띄어쓰기 2칸
    url: jdbc:h2:tcp://localhost/~/board #4칸
    username: sa
    password:
    driver-class-name: org.h2.Driver

  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

  jpa: #띄어쓰기 2칸
    hibernate: #띄어쓰기 4칸
      ddl-auto: create #띄어쓰기 6칸
    properties: #띄어쓰기 4칸
      hibernate: #띄어쓰기 6칸
        # show_sql: true #띄어쓰기 8칸
        format_sql: true #띄어쓰기 8칸

  mvc:
    hidden-method:
      filter:
        enabled: true

  devtools:
    livereload:
      enabled: true
    thymeleaf:
      cache: false
    restart:
      enable: false #운영에서는 제거

logging:
  level:
    org.hibernate.SQL: debug 

upload:
  path: C:/study/file/
- multipart 파일 업로드시 max size를 설정하는 부분을 추가하였습니다.

 

 

결과화면

 

 

 

다음장에서 게시판 파일 정보 테이블을 만들면서 File 테이블과 연관관계를 개발 해보겠습니다.

 

다음글보기

https://aamoos.tistory.com/686

 

[Spring jpa] 15. 게시판 만들기 - 첨부파일

이전글 보기 https://aamoos.tistory.com/685 [Spring Jpa] 14. 게시판 만들기 - 멀티파일 업로드 목표 - 파일을 여러개 올릴수 있는 input을 추가한후 멀티파일 업로드 기능을 구현하려고 합니다. 이번장에서는

aamoos.tistory.com

복사했습니다!