Loading...

Spring/Tistory / / 2022. 4. 27. 00:10

블로그-V3. 썸네일 파일 업로드

반응형

파일 업로드 시 yml 파일 설정

 

dev파일에 저장 경로를 설정해줘야 한다.

 

사진 저장을 내 프로젝트 내부가 아닌 외부에 저장하겠다고 알려주는 것이다.

 

C 드라이브로 경로를 지정하면 그 경로에 파일을 쓸 수 있는지 권한이 필요하다.

 

윈도우 폴더에 파일 쓰기 거부당한다면 아래 링크를 참고하자.

 

https://blog.daum.net/khkim6001/649

 

[서대문구 홍은동,홍제동,영천동,천연동 컴퓨터수리,노트북수리] 관리자 권한이 필요합니다 라

요즘 메이커 컴퓨터는 대부분 윈도우8 이나 윈도우8.1이 설치되어 출시가 됩니다. 윈도우7 다음버전으로 나온것이 윈도우8이고 윈도우8이 사용상 불편해서 보완해서 나온것이 윈도우8.1입니다.

blog.daum.net

 

C:\Users\green\upload 해당 경로로 지정해줄 것이다.

 

윈도우는 파일 이동시에 \를 사용하는데 리눅스는 /를 사용한다.

 

프로그래밍 언어는 OS의 경로를 따라가는지

독자적으로 파일 경로를 인식하는 키워드가 있는지가 중요하다.

 

스프링은 독자적으로 파일 경로를 인식하는 키워드가 있는데 /이다.

 

file:
  path: C:/Users/green/upload/

 

경로 뒤에 파일을 넣어줄 것이기 때문에 맨 뒤에 슬래쉬를 붙여줬다.

 

이를 똑같이 prod 파일에 넣어줄 건데 prod는 EC2서버에서 돌건데 리눅스는 최상위 폴더가 C가 아닌 /이다.

 

file:
  path: /home/ubuntu/upload/

 

 

 

통신은 무조건 램에 저장된다.

아직까지 I/O가 일어나지 않은 것이다. 

 

램에 저장되어있는 파일명만 추출해낸 것이다.

 

@Transactional
public void 게시글쓰기(PostWriteReqDto postWriteReqDto) {
    // 1. 이미지 파일 저장 (UUID 변경)
    UUID uuid = UUID.randomUUID();
    String originalFilename = postWriteReqDto.getThumnailFile().getOriginalFilename(); // 충돌나니까 UUID 사용
    String thumnail = uuid + "_" + originalFilename;
    
    // 2. 이미지 파일명을 Post 오브젝트 thumnail에 옮기기
    // 3. title, content도 Post 오브젝트에 옮기기
    // 4. userId(user 오브젝트)도 Post 오브젝트에 옮기기
    // 5. categoryId(category 오브젝트)도 Post 오브젝트에 옮기기
    // 6. save
}

 

java.nio.file 라이브러리가 너무 잘되어있어서 하드디스크에 파일을 쓰는 게 아주 간편하다.

파일 경로를 지정하기 위해서 yml에 설정해둔 path를 가져와야 한다.

 

@Value("${file.path}") // yml에 등록한 키 값 찾을 때 사용하는 어노테이션
private String uploadFolder;

 

이 path 뒤에 파일명을 붙여주는데 uuid를 적용시킨 파일명을 _으로 구분하여 넣어준다.

이 파일을 쓰기만 하면 된다.

 

Files.write( ) 메서드를 사용하는데 파일 경로와 파일을 직접 지정해주는데

이 파일을 바이트로 써줘야 한다.

 

이제 이 파일을 DB에 save 해주면 끝!

 

@Transactional
public void 게시글쓰기(PostWriteReqDto postWriteReqDto) {
	// 1. 이미지 파일 저장 (UUID 변경)
	UUID uuid = UUID.randomUUID();
	String originalFilename = postWriteReqDto.getThumnailFile().getOriginalFilename(); // 충돌나니까 UUID 사용
	String thumnail = uuid + "_" + originalFilename;
	try {
		Path filePath = Paths.get(uploadFolder + thumnail); // I/O 작업
		Files.write(filePath, postWriteReqDto.getThumnailFile().getBytes());
	} catch (Exception e) {
		throw new CustomException("파일 업로드 실패");
	}
}

 

save 할 때 post타입으로 INSERT 해줘야 하니까 postWriteReqDto에 toEntity 메서드를 만들어주자

 

public Post toEntity(String thumnail, User principal, Category category) {
    Post post = new Post();
    post.setTitle(title);
    post.setContent(content);
    post.setThumnail(thumnail);
    post.setUser(principal);
    post.setCategory(category);
    return post;
}

 


 

@PostMapping("/s/post")
public String write(PostWriteReqDto postWriteReqDto,
        @AuthenticationPrincipal LoginUser loginUser) {

    postService.게시글쓰기(postWriteReqDto, loginUser.getUser());

    return "redirect:/user/" + loginUser.getUser().getId() + "/post";
}
@Transactional
public void 게시글쓰기(PostWriteReqDto postWriteReqDto, User principal) {
    // 1. 이미지 파일 저장 (UUID 변경)
    UUID uuid = UUID.randomUUID();
    String originalFilename = postWriteReqDto.getThumnailFile().getOriginalFilename(); // 충돌나니까 UUID 사용
    String thumnail = uuid + "_" + originalFilename;
    try {
        Path filePath = Paths.get(uploadFolder + thumnail); // I/O 작업
        Files.write(filePath, postWriteReqDto.getThumnailFile().getBytes());
    } catch (Exception e) {
        throw new CustomException("파일 업로드 실패");
    }

    // Category category = new Category();
    // category.setId(postWriteReqDto.getCategoryId());

    // 2. 이미지 파일명을 Post 오브젝트 thumnail에 옮기기
    // 3. title, content도 Post 오브젝트에 옮기기
    // 4. userId(user 오브젝트)도 Post 오브젝트에 옮기기
    // 5. categoryId(category 오브젝트)도 Post 오브젝트에 옮기기
    // 6. save

    Optional<Category> categoryOp = categoryRepository.findById(postWriteReqDto.getCategoryId());

    if (categoryOp.isPresent()) {
        Post post = postWriteReqDto.toEntity(thumnail, principal, categoryOp.get());
        postRepository.save(post);
    } else {
        throw new CustomException("해당 카테고리가 존재하지 않습니다.");
    }

    // 오브젝트(ORM)가 아닌 FK를 직접 주입하는 방법의 단점 : 없는 FK가 들어올 수 있다 -> 막을 수 없음
    // postRepository.mSave(postWriteReqDto.getCategoryId(), principal.getId(),
    // postWriteReqDto.getTitle(),
    // postWriteReqDto.getContent(), thumnail);
}

 

코드 리팩토링하자.

하나의 트랜잭션은 여러 가지 일을 한번에 처리하여 서비스에는 여러가지 로직이 공존한다.

 

여러가지 일을 처리하는 서비스의 단점은 디버깅하여 오류를 잡기가 힘들다.

 

파일 업로드 로직을 분리해주자.

 

Util 폴더에다가 파일을 쓰고 경로를 리턴해주는 클래스를 만들어준다.

이 메서드에 업로드할 폴더의 경로 저장할 파일 받아줘야 한다.

 

package site.metacoding.blogv3.util;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

import org.springframework.web.multipart.MultipartFile;

import site.metacoding.blogv3.handler.ex.CustomException;

public class UtilFileUpload {

    public static String write(String uploadFolder, MultipartFile file) {

        // 파일을 쓰고 난 뒤 그 경로를 리턴해주는 메서드이다.
        UUID uuid = UUID.randomUUID();
        String originalFilename = file.getOriginalFilename(); // 충돌나니까 UUID 사용
        String uuidFilename = uuid + "_" + originalFilename;
        try {
            Path filePath = Paths.get(uploadFolder + uuidFilename); // I/O 작업
            Files.write(filePath, file.getBytes());
        } catch (Exception e) {
            throw new CustomException("파일 업로드 실패");
        }

        return uuidFilename;
    }
}

 

그 다음에 서비스에서 UtilFileUpload.write 메서드를 호출해주면

파일을 하드에 저장한 뒤 파일명에 UUID를 적용시키고 저장된 경로를 리턴해준다.

 

// 하나의 서비스는 여러가지 일을 한번에 처리한다. (여러가지 일이 하나의 트랜잭션이다.)
@Transactional
public void 게시글쓰기(PostWriteReqDto postWriteReqDto, User principal) {

    // 1. 이미지 파일 저장 (UUID 변경) 후 경로 리턴 받기
    String thumnail = UtilFileUpload.write(uploadFolder, postWriteReqDto.getThumnailFile());

    // 2. 카테고리 있는지 확인
    Optional<Category> categoryOp = categoryRepository.findById(postWriteReqDto.getCategoryId());

    if (categoryOp.isPresent()) {
        Post post = postWriteReqDto.toEntity(thumnail, principal, categoryOp.get());
        postRepository.save(post);
    } else {
        throw new CustomException("해당 카테고리가 존재하지 않습니다.");
    }
}

 

다음 시간에 write 한 게시글 목록을 뿌려줄 건데

이때 썸네일을 찾을 때

<img src=""></img>

이미지의 경로를 찾을 수 없다.

 

내부에서 /를 사용하면 무조건 static 폴더를 찾기 때문이다.

 

외부 경로를 찾는 방법을 배울 것이다.

 

[출처]

 

https://cafe.naver.com/metacoding

 

메타코딩 : 네이버 카페

코린이들의 궁금증

cafe.naver.com

메타 코딩 유튜브

https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9

 

메타코딩

문의사항 : getinthere@naver.com 인스타그램 : https://www.instagram.com/meta4pm 깃헙 : https://github.com/codingspecialist 유료강좌 : https://www.easyupclass.com

www.youtube.com

 
반응형