스프링 부트- 파일

김정현·2024년 8월 9일
1

포트폴리오

목록 보기
4/8

File

Upload

업로드 커맨드 객체에 파일 추가

 form.setFiles(files);

파일 업로드 전 처리

 beforeProcess.process(form); 

- beforeProcess

//process

이미지만 포함되어있어야함
이미지가 아닌 경우 예외 발생

 // 업로드된 파일에서 이미지만 포함되어 있는지 체크
        if (form.isImageOnly()) {
            for (MultipartFile file : files) {
                String contentType = file.getContentType();
                // 이미지이면 image/png, image/gif ..
                // 이미지가 아닌 파일이 포함된 경우
                if (!contentType.contains("image/")) {
                    throw new FileTypeException(HttpStatus.BAD_REQUEST);
                }
            }
        }

단일 파일업로드 인경우 기존 파일 삭제 후 새로운 파일 set

 if (form.isSingle()) {
            deleteService.delete(form.getGid(), form.getLocation(), FileStatus.All);
            form.setFiles(new MultipartFile[] { files[0] });
        }

 List<FileInfo> items = uploadService.upload(files, form.getGid(), form.getLocation());

/**
1. 파일 정보 저장
2. 파일을 서버로 이동
3. 이미지이면 썸네일 생성
4. 업로드한 파일 목록 반환
*/


FileUploadService

gid 설정(form.getGid()) 비어있으면 랜덤으로

gid = StringUtils.hasText(gid) ? gid : UUID.randomUUID().toString();

매개변수로 가져온 파일의 정보들 build 후 엔티티 데이터베이스로 플러시 (seq는 GeneratedValue)

 // 1. 파일 정보 저장
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();   // 업로드 파일 원래 이름
            String contentType = file.getContentType();   // 파일 형식 (image/jpeg)
            String extension = fileName.substring(fileName.lastIndexOf("."));   // 확장자

            FileInfo fileInfo = FileInfo.builder()
                    .gid(gid)
                    .location(location)
                    .fileName(fileName)
                    .contentType(contentType)
                    .extension(extension)
                    .build();

            fileInfoRepository.saveAndFlush(fileInfo);

uploadPath 지정

 // 2. 파일을 서버로 이동
            long seq = fileInfo.getSeq();
            String uploadDir = properties.getPath() + "/" + (seq % 10L);    // 디렉토리 분산
            File dir =  new File(uploadDir);    // 경로와 파일 사이 연결을 표현
            if (!dir.exists() || !dir.isDirectory()) {
                dir.mkdir();
            }

            String uploadPath = uploadDir + "/" + seq + extension;

uploadPath 경로로 저장
만들어 뒀던 uploadedFiles 리스트에 저장

try {                                       // transferTo 메서드는 파일을 지정된 경로로 이동, 복사
                file.transferTo(new File(uploadPath));  // 업로드된 파일을 uploadPath 경로에 저장
                uploadedFiles.add(fileInfo);    // 업로드 성공 파일 정보
            } catch (IOException e) {
                e.printStackTrace();
                // 파일 이동 실패시 정보 삭제
                fileInfoRepository.delete(fileInfo);
                fileInfoRepository.flush();
            }

infoservice로 리스트 각각 요소에 대해서 addFileInfo 실행

uploadedFiles.forEach(fileInfoService::addFileInfo);

FileInfoService

매개변수 item은 아까 builder했던 FileInfo 엔티티
item entities에 fileUrl, filePath 저장

public void addFileInfo(FileInfo item) {
        String fileUrl = getFileUrl(item);
        String filePath = getFilePath(item);

        item.setFileUrl(fileUrl);
        item.setFilePath(filePath);
    }
  • 브라우저에서 이미지 파일에 직접 접근할 주소(fileUrl)
    : 파일이 서버에 저장된 후, 사용자가 해당 파일을 웹에서 접근하기 위한 경로
    -이 URL을 사용하면 브라우저에서 파일에 접근하거나 다운로드할 수 있음.
  • 서버에 이미지를 업로드할 경로를 생성(filePath): 파일이 서버에 실제로 저장되는 물리적 위치
    // 브라우저 접근 주소
    public String getFileUrl(FileInfo item) {
        return request.getContextPath() + properties.getUrl() + getFolder(item.getSeq()) + "/" + getFileName(item);
    }

    // 서버 업로드 경로
    public String getFilePath(FileInfo item) {
        return properties.getPath() + "/" + getFolder(item.getSeq()) + "/" + getFileName(item);
    }
    
     public String getFolder(long seq) {
        return String.valueOf(seq % 10L);
    }
    
    public String getFileName(FileInfo item) {
        String fileName = item.getSeq() + Objects.requireNonNullElse(item.getExtension(), "");
        return fileName;
    }

uploadPath vs filePath

  • uploadPath: 파일 업로드 시 파일이 저장될 구체적인 경로를 나타내며, 업로드 작업 중에만 사용. 파일을 서버에 저장하는 시점에 활용되는 경로.

  • filePath: 파일이 서버에 저장된 후, 파일의 위치를 추적하고 관리하기 위해 사용되는 경로. 데이터베이스나 다른 시스템에서 파일의 위치를 참조할 때 사용.


AfterFileUploadProcess

서버에 저장은 됐음.

afterProcess.process(form); // 파일 업로드 후처리

사용자가 입력했던 파일 form 을 가지고 gid, location 가지고 doneService.process 실행

public void process(RequestUpload form) {

        // 파일 업로드 직후 완료 처리
        if (form.isDone()) {
            doneService.process(form.getGid(), form.getLocation());
        }
    }

location 필드

: 파일 업로드의 위치나 목적지를 나타내는 데 사용
: 파일이 저장될 경로, 논리적 위치, 업로드 대상, 또는 컨텍스트 정보를 지정하는 데 활용


FileUploadDoneService

infoService.getList 로부터 FileInfo 리스트 가져옴
, 그룹 작업 완료 여부를 업데이트함

public void process(String gid, String location) {
        List<FileInfo> items = infoService.getList(gid, location, FileStatus.All);

FileInfoService

사용자가 입력한 파일의 form. gid, location 를 통해서 andBuilder 패턴으로 gid, location, status를(DONE)인것만 걸러서 담아줌

그리고 생성 날짜로 정렬함.

public List<FileInfo> getList(String gid, String location, FileStatus status) {

        QFileInfo fileInfo = QFileInfo.fileInfo;
        BooleanBuilder andBuilder = new BooleanBuilder();
        andBuilder.and(fileInfo.gid.eq(gid));

        if (StringUtils.hasText(location)) {
            andBuilder.and(fileInfo.location.eq(location));
        }

        if (status != FileStatus.All) {
            andBuilder.and(fileInfo.done.eq(status == FileStatus.DONE));
        }

        List<FileInfo> items = (List<FileInfo>)infoRepository.findAll(andBuilder, Sort.by(asc("createdAt")));

        // 2차 추가 데이터 처리
        items.forEach(this::addFileInfo);

        return items;
    }

    public List<FileInfo> getList(String gid, String location) {
        return getList(gid, location, FileStatus.DONE);
    }

    public List<FileInfo> getList(String gid) {
        return getList(gid, null, FileStatus.DONE);
    }

FileUploadDoneService

FileInfo 엔티티에 항목을 status가 DONE 인 항목들만 걸렀으니
done을 true로 설정해주고
플러시함

// process에서
        items.forEach(i -> i.setDone(true));

        repository.saveAllAndFlush(items);
    }

AfterFileUploadProcess

 public void process(RequestUpload form) {

        // 파일 업로드 직후 완료 처리
        if (form.isDone()) {
            // status가 DONE인 항목들만 거르고 , 그 요소들에 대해서 setDone(true)로 설정함
            doneService.process(form.getGid(), form.getLocation());
        }
    }

FileManager.js 의 존재 이유

마치 spring 프로젝트 백엔드 단계에서 validation과 커맨드객체를 활용하여 검증하듯이
JS는 프론트 단계에서 파일이 백엔드로 이루어지기전에 검증하여 사용자에게 즉각적인 피드백을 보여준다.

프론트와 백엔드 단계에서 적절한 검증이 이루어짐에 따라 서버로의 불필요한 데이터 전송과 더욱 빠른 데이터 처리를 보여준다.

요약

  • 프론트(요청 관련)

    commonLib, FileManager JS에서는
    사용자가 입력한것을 검증하고 가공해서 FormData에 담고
    서버로 보내주는 역할을 한다.
    예) 파일이 두개 담기지는 않았는가? , 이미지 파일이 맞는가?

commonLib: 클라이언트 측에서 서버로 HTTP 요청을 보내고, 서버의 응답을 처리하는 공통 기능을 제공. 이 함수는 요청의 메소드, URL, 본문 데이터, 헤더 등을 설정하며, 서버와의 통신을 간소화하고 일관성 있게 처리할 수 있도록
도움.

Ajax 기능 (실시간으로 페이지를 필요한 부분만 로드함)

CSRF 토큰을 포함시키고, fetch를 통해 서버로 비동기 요청을 보냄. 서버는 이 토큰을 검증하여 요청의 유효성을 확인.

  • 백엔드(응답 관련)

    스프링으로 처리하며 데이터베이스에 담기 위해서 2차적인 검증을 하고 검증한 데이터를 가공하여 데이터베이스에 담고 응답 본문에 담아서 사용자에게 출력 및 활용할 수 있게끔 한다.

0개의 댓글