have a good time 2021. 12. 14. 17:00

<upload.jsp> 

 

   <!--사진 업로드페이지 중앙배치-->
        <main class="uploadContainer">
           <!--사진업로드 박스-->
            <section class="upload">
               
               <!--사진업로드 로고-->
                <div class="upload-top">
                    <a href="home.html" class="">
                        <img src="/images/logo.jpg" alt="">
                    </a>
                    <p>사진 업로드</p>
                </div>
                <!--사진업로드 로고 end-->
                
                <!--사진업로드 Form-->
                <form class="upload-form" >
                    <input  type="file" name="file"  onchange="imageChoose(this)"/>
                    <div class="upload-img">
                        <img src="/images/person.jpeg" alt="" id="imageUploadPreview" />
                    </div>
                    
                    <!--사진설명 + 업로드버튼-->
                    <div class="upload-form-detail">
                   		 <input type="text" placeholder="사진설명" name="caption">
                        <button class="cta blue">업로드</button>
                    </div>
                    <!--사진설명end-->
                    
                </form>
                <!--사진업로드 Form-->
            </section>
            <!--사진업로드 박스 end-->
        </main>
        <br/><br/>
	
	<script src="/js/upload.js" ></script>

 

실제사이트 화면

 

 

upload.jsp 파일에서 

 

1) 이미지 첨부

     <input  type="file" name="file"  onchange="imageChoose(this)"/>

이 부분이, 위 그림에서 "파일선택" 부분.

즉, "파일선택"을 클릭하면 

창이 하나 뜨면서 컴퓨터에 있는 자료를 선택할 수 있게 된다.

 

 

 

2) 사진 설명 글

 <input type="text" placeholder="사진설명" name="caption">

이 부분은 아래의 "사진설명" 이라고 써있는 칸이다.

여기에 사진 설명을 적고 업로드 버튼 누르면 됨.

 

 

 

 

특히, "파일선택" 을 누른 뒤 home.jpg 라는 파일을 선택했더니, 아래와 같이 home.jpg 그림으로

화면이 바뀌었다.

 

 

 

 

 

 

이는 update.js 라는 파일에 구현되어 있다.

 

function imageChoose(obj) {
	let f = obj.files[0];

	if (!f.type.match("image.*")) {
		alert("이미지를 등록해야 합니다.");
		return;
	}

	let reader = new FileReader();
	reader.onload = (e) => {
		$("#imageUploadPreview").attr("src", e.target.result);
	}
	reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
}

 

 

 

<Image.java> 파일

 

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Image {

	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	private String caption; 
	private String postImageUrl; 
	
	@JoinColumn(name = "userId")
	@ManyToOne(fetch = FetchType.EAGER) 
	private User user; 
	
	private LocalDateTime createDate;
	
	@PrePersist
	public void createDate() {
		this.createDate = LocalDateTime.now();
	}

}

 

caption : 사진 설명

 

postImageUrl : 사진 경로

우리는, 사용자가 등록한 사진을 받아서 서버의 특정 폴더에 저장할 것. 데이터베이스에 그 경로를 insert

그래서 자세히 보면 파일 형태가 아니라, String 타입

 

 

 

예를 들어 서버의 upload 라는 폴더에 사용자가 업로드한 이미지 파(a.jpg)일이 저장되고,

데이터베이스에는, 이 경로, 즉 서버의 upload 폴더 경로만 저장이 됨.(예를 들어 /upload/a.jpg)

 

 

 

 

user : 누가 사진 업로드 했나

(한 명의 user(1)는 여러개의 Image(N)를 올릴 수 있으므로,

한 개의 Image(1)는 한 명의 user(1) 가 등록

-> @ManyToOne

 

이 때 user는 Image 테이블에서 FK 로 저장되는데,

그 때 FK의 이름은 

@JoinColumn(name = "userId")

이렇게 우리가 정해줌.

 

그래서 데이터베이스에서 Image 테이블을 확인해보면,

userId 는 아래처럼 userId 라는 FK 로 들어와 있음

 

 

	private LocalDateTime createDate;
	
	@PrePersist
	public void createDate() {
		this.createDate = LocalDateTime.now();
	}

시간 : 데이터베이스에는 그 데이터가 언제 들어왔는지, 항상 시간을 기록해야 한다고 함.

 

 

 

<ImageRepository.java> 파일

 

public interface ImageRepository extends JpaRepository<Image, Integer>{

}

 

위에서 Image.java 에서

postImageUrl , 즉 사진 경로를 입력한다고 했지만

일단은 사용자가 이미지 업로드 하면 이미지 파일을 받아야 함.

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

그래서 데이터들을 받기 위해

<ImageUploadDto.java> 파일 만듦

 

@Data
public class ImageUploadDto {

	private MultipartFile file;
	private String caption;	
	
}

 

우리가 upload.jsp 파일에서 input 태그로 받는 2개의 값이, file(이미지 파일)과 caption(사진설명) 이므로 

두개의 변수를 만듦

 

file 을 받을 때 MultipartFile 타입으로 받을 수 있음

 

 

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

<ImageController.java> 파일

 

 

 

 

	@PostMapping("/image")
	public String imageUpload(ImageUploadDto imageUploadDto, @AuthenticationPrincipal PrincipalDetails principalDetails) {
		
		imageService.사진업로드(imageUploadDto, principalDetails);
		return "redirect:/user/"+principalDetails.getUser().getId();
	}

매개변수로 @AuthenticationPrincipal PrincipalDetails principalDetails

이 값을 받는 이유는,

로그인한 사용자가 이미지를 올리도록 하기 위해.

 

 

사진 업로드가 끝난 다음에 

/user/id(사용자 id 번호)

페이지로 이동되도록 하기 위해 

 

아래와 같이 입력

return "redirect:/user/"+principalDetails.getUser().getId();

 

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

<ImageService.java> 파일 - 실제 로직은 service 에서 구현

@Service
public class ImageService {
	
	@Value("${file.path}")
	private String uploadFolder;
	
	@Transactional
	public void 사진업로드(ImageUploadDto imageUploadDto, PrincipalDetails principalDetails) {
		UUID uuid = UUID.randomUUID(); 
		String imageFileName = uuid+"_"+imageUploadDto.getFile().getOriginalFilename(); 
		System.out.println("이미지 파일이름 : "+imageFileName);
		
		Path imageFilePath = Paths.get(uploadFolder+imageFileName);
		
		try {
			Files.write(imageFilePath, imageUploadDto.getFile().getBytes());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

<파일 이름>

String imageFileName = imageUploadDto.getFile().getOriginalFilename();

이렇게 하면 실제 파일 이름 (예를 들어 1.jpg) 이

imageFileName 변수에 들어감

 

여기에, 똑같은 파일 이름이 들어올 수 있기 때문에, UUID 사용 

-> 유일성이 보장됨 (중복이 안됨)

더보기

UUID : 네트워크 상에서 고유성이 보장되는 id를 만들기 위한 표준 규약(범용 고유 식별자)

 

참고 자료 : https://mattmk.tistory.com/31

그래서 아래와 같이 변경

		UUID uuid = UUID.randomUUID(); 
		String imageFileName = uuid+"_"+imageUploadDto.getFile().getOriginalFilename();

 

위에서 System.out.println 으로, imageFileName이 출력되도록 했는데,

만약 logo.jpg 파일을 업로드 했다면,

sts(이클립스) 콘솔창에 아래와 같이 나옴

검은색 부분은 UUID 값이 나온 것임

 

 

<이미지 실제 저장>

 

1. 이미지 저장 경로 지정 - application.yml 파일에 file: path: 지정

Paths.get(실제 저장할 경로 입력)

 

application.yml 파일에 이미지를 실제 저장할 경로를 적어놓음

 

이런식으로 적어 놓았는데, C드라이브 아래 경로를 지정해 두었기 때문에,

만약 C드라이브 내부의 Hello 라는 폴더내부의 Hi 폴더 내부의 upload 폴더에 저장하려면

path: C:/Hello/Hi/upload/ 

이런식으로 입력 

(실제 우리 컴퓨터에 폴더 만들어야 함. 그 폴더로 이미지 파일 들어옴)

(그리고 스프링부트 프로젝트가 저장되는 폴더에 저장하면 좋겠음.

예를 들어 현재 진행 중인 스프링 프로젝트가 Hello 폴더에 저장된다면 위와 같이 경로 지정)

더보기

(특히 application.yml 파일은 띄어쓰기가 중요한데,

file 문자 앞에 공백 없고, 맨 앞칸부터 쓴 것이며, path 는 두 칸 뛰고 입력

 

file:

  path: 

 

)

 

그리고 이 값은 application.yml 에 이미 정해져 있는 키 값들이 아니고, 내가 만든 키 값임.

기존에 스프링 부트 설정할 때 데이터베이스 등 지정할 것들은 이미 지정되어 있는 키 값들인데,

이것은 내가 만든 것  

 

2. 서버(현재는 우리 컴퓨터)에 이미지 저장 가능하도록 지정

- application.yml 파일에 servlet: multipart: enabled: 지정

 

 

multipart 타입으로 사진 받겠다.(multipart, enabled)

입력 가능한 파일 최대 크기 : 2MB

 

더보기

spring:

  servlet:

    enabled:

      max-file-size:

 

이런 식으로 입력 받음

 

spring 문자 앞에 공백 없는 첫 칸에 입력했고,

그 다음 문자부터 두 칸씩 더 들여쓰기

 

위의 사진에 spring 이 없는 이유는, 데이터베이스 등 다른 것들 설정했기 때문에

거리가 떨어져 있어서 위에 것만 입력

 

이렇게 지정한 경로를 path 에 지정해주려는데, 

ImageService.java 파일에다가

 

	@Value("${file.path}")
	private String uploadFolder;

 

이렇게 지정해주면 됨. 위에서 application.yml 파일에서 file: path: 에다가 지정했기 때문에

위와 같이 지정했음

 

 

ImageService.java 등 이 경로를 사용하려는 파일에서 

private String uploadFolder = "C:/Hello/Hi/upload/";

이렇게 적어도 되지만, 매번 적는 것보단 application.yml 에 지정해주고 사용하면 되므로 편리

 

 

그래서 최종적으로 이미지 path 는 아래와 같이, 

내가 만든 이미지 경로 (uploadFolder)와 

이미지 이름 (imageFileName) 을 같이 넣어주면 됨

	Path imageFilePath = Paths.get(uploadFolder+imageFileName);

 

 

그 다음 이미지 업로드가 진행되도록 아래와 같은 코드 

	Files.write(imageFilePath, imageUploadDto.getFile().getBytes());

첫번째 매개변수 : path

두번째 매개변수 : 실제 이미지 파일(byte 화 해서 넣어야 함)

세번째 매개변수 : 생략했음 여기서는.

 

 

여기서 try catch 를 사용한 이유

		try {
			Files.write(imageFilePath, imageUploadDto.getFile().getBytes());
		} catch (Exception e) {
			e.printStackTrace();
		}

통신(외부에서 데이터 받아오는 등), I/O (하드디스크에 기록하거나 읽거나)

-> 예외 발생 가능.

-> 이런 것들은 컴파일 시에 잡지 못하기 때문에 (런타임 시에만 알 수 있음)

 

 

<upload.jsp> 파일

사진업로드 form 태그에 action 지정

 

 

      <form class="upload-form" action="/image" method="post" enctype="multipart/form-data">

여기서, 아래와 같이 지정한 이유는 

enctype="multipart/form-data"

default 값은 application/x-www-form-urlencoded

방식인데 여기서는 

input 2 : caption 부분은 key, value 형태(x-www-form-urlencoded) 로 데이터가 날아가는데,

iinput1 : 이미지 파일 부분은 실제 파일임. 파일은 byte 화 해서 날아가는데, 

 

그러니깐, 서로 다른 타입을 전송하는데

여러 가지 종류 타입을 묶어서 전송하도록 위와 같이 지정

 

 

 

이렇게 한 다음, 사이트 사진 업로드 화면에서 

 

 

사진 업로드 하면, 실제 위에서 지정한 C:/Hello/Hi/upload/ 폴더에

우리가 업로드한 이미지 파일이 들어옴

 

데이터베이스에 위 사진 경로만 저장하면 됨 - 다음 글에서 계속

 

 

참고 자료 : 이지업 강의 사이트 "스프링부트 SNS프로젝트 - 포토그램 만들기"