20240223 Spring 9 - 메일(보내기 기능), 파일 업로드

Leafy·2024년 2월 23일
1

중앙_자바

목록 보기
45/76

메일 보내기 기능

pom.xml

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-email -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-email</artifactId>
			<version>1.5</version>
		</dependency>

apache가 제공
spring이 제공하는 것도 있다.

SMTP - 메일 보내는 프로토콜

db 안가고 그냥 전송(db에 입력된 받는 사람이 아니라 받는 사람 이메일을 폼에서 입력받아서 전송하는 방식으로 단순화,,)

MailService.java에 전송하는 코드 적음.
MailController.java post에는 단순하게 mailService.sendMail(email, title, content);

@Service
public class MailService {

	//메일 보내기
	public void sendMail(String email, String title, String content) throws EmailException {
		String emailAddr = MailInfo.emailId; //보내는 사람 이메일 = outlook
		String name = "하츄초콜릿"; //보내는 사람 이름
		String pw = MailInfo.emailPw; //보내는 사람 비번 = outlook 만든거
		String host = "smtp-mail.outlook.com"; //아웃룩 호스트
		int port = 587; //아웃룩 포트
		
		SimpleEmail mail = new SimpleEmail(); //여기다 조립
		mail.setCharset("UTF-8"); //언어셋 인코딩
		mail.setDebug(true); //디버그 - 이유는 잘 몰라도 결과 보기위해 적어주자
		mail.setHostName(host); //호스트 = 아웃룩(보내는사람꺼)
		mail.setAuthentication(emailAddr, pw);//보내는 사람 이메일주소, 비번
		mail.setSmtpPort(port);//보내는 쪽의 포트
        mail.setStartTLSEnabled(true);
		mail.setFrom(emailAddr, name); //보내는 사람의 주소, 이름 -> EmailException throw해주자 (이 메서드 호출한 Controller가 에러 -> 걔도 던지자)
		mail.addTo(email); //받는 사람 === 파라미터로 받은거
		mail.setSubject(title); //메일 제목 === 파라미터로 받은거
		mail.setMsg(content); //메일 내용 === 파라미터로 받음~
		mail.send(); //발송
	}
}

아웃룩 계정이 막혀버렷다..
-> 풀고 된다!

HTML 메일 보내기

지금까지는 Text 메일만 보내졌다.
내용에 html 태그도 먹고 파일 보내기도 가능

public void sendHTMLMail(String email, String title, String content) throws EmailException {
	String emailAddr = MailInfo.emailId; //보내는 사람 이메일 = outlook
	String name = "하츄초콜릿"; //보내는 사람 이름
	String pw = MailInfo.emailPw; //보내는 사람 비번 = outlook 만든거
	String host = "smtp-mail.outlook.com"; //아웃룩 호스트
	int port = 587; //아웃룩 포트
	
	HtmlEmail mail = new HtmlEmail();
	mail.setCharset("UTF-8"); //언어셋 인코딩
	mail.setDebug(true); //디버그 - 이유는 잘 몰라도 결과 보기위해 적어주자
	mail.setHostName(host); //호스트 = 아웃룩(보내는사람꺼)
	mail.setAuthentication(emailAddr, pw);//보내는 사람 이메일주소, 비번
	mail.setSmtpPort(port);//보내는 쪽의 포트
	mail.setStartTLSEnabled(true);
	mail.setFrom(emailAddr, name); //보내는 사람의 주소, 이름 -> EmailException throw해주자 (이 메서드 호출한 Controller가 에러 -> 걔도 던지자)
	mail.addTo(email); //받는 사람 === 파라미터로 받은거
	mail.setSubject(title); //메일 제목 === 파라미터로 받은거
	mail.setMsg(content); //메일 내용 === 파라미터로 받음~
	
	//파일 첨부도 가능
	EmailAttachment file = new EmailAttachment();
	file.setPath("c:\\temp\\img.png");
	mail.attach(file);
	
	mail.send(); //발송
}

파일 업로드

pom.xml

<!-- 파일업로드 -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.3</version>
</dependency>
  • form태그의 enctype="multipart/form-data" <- 이거는 MIME 타입 같아 보인다
<form action="./file" method="post" enctype="multipart/form-data">
	<div class="input-group">
		<input type="file" name="file1" class="form-control" id="inputGroupFile04" aria-describedby="inputGroupFileAddon04" aria-label="Upload">
		<button class="btn btn-outline-secondary" type="submit" id="inputGroupFileAddon04">Button</button>
	</div>
</form>
@PostMapping("/file")
public String file(@RequestParam("file1") MultipartFile upFile) {
	System.out.println("파일 이름 : " +upFile.getOriginalFilename());
	System.out.println("파일 사이즈 : " +upFile.getSize());
	System.out.println("파일 타입 : " +upFile.getContentType());
	return "redirect:/file";
}
  • Multipart 설정 에러 (xml 설정 필요)
    : 객체가 없어서 설정 못한다~
    servlet-context.xml에서
<beans:import resource="classpath:/spring/file-context.xml"/>


그냥 하면 이렇게 뜬다

file-context.xml

<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" " id="multipartResolver">
  <property name="defaultEncoding" value="UTF-8"></property>
  <property name="maxUploadSizePerFile" value="52428800"></property>
  <property name="maxUploadSize" value="104857600"></property>
</bean>

파일 사이즈 1024 * 1024 ... -> 풀어서 쓴 거다

재기동

아주 간단한 파일 업로드

c:/temp/upfile 경로에 업로드한 파일을 저장해준다.

@PostMapping("/file")
public String file(@RequestParam("file1") MultipartFile upFile) {
	System.out.println("파일 이름 : " +upFile.getOriginalFilename());
	System.out.println("파일 사이즈 : " +upFile.getSize());
	System.out.println("파일 타입 : " +upFile.getContentType());
	
	//Real upload
	File f = new File("c:/temp/upfile", upFile.getOriginalFilename()); //파일경로, 파일이름
	try {
		upFile.transferTo(f); //multipartfile의 메소드
	} catch (IllegalStateException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	return "redirect:/file";
}

책 p488~p604

스토리지 서버: 파일 저장

p492 Java 설정 사용 (안함)
p494 <form> 방식 (우리가 하는거)
-p496 브라우저 지원(요즘다됨)
p497 여러개는 MultiPartFile[] 배열
p499 .transferTo()
p505 고려 사항(아주 중요)
p506 js로 막기


파일업로드를 한 걸 db에 저장.
실제 파일을 저장할 건 아니고 파일 이름만.

파일 종류 제한

<form action="./file" method="post" enctype="multipart/form-data">
            	<div class="input-group">
				  <input type="file" accept="image/png, image/jpeg" name="file1" class="form-control" id="inputGroupFile04" aria-describedby="inputGroupFileAddon04" aria-label="Upload">
				  <button class="btn btn-outline-secondary" type="submit" id="inputGroupFileAddon04">Button</button>
				</div>
            	</form>

accept="image/png, image/jpeg" 하면 찾아보기... 에서 png와 jpg 파일들만 들어온다.
jpg라고 하니까 안되더라
최종: accept="image/*" 모든 이미지 형식


근데 어차피.. 드롭다운 모든파일하면 됨..


js로 막아줘야 한다.

js로 파일 종류 제한하기

p506
크기제한, 이미지냐

중복 이름 처리(제일 중요)

같은 이름이면 나중껄로 덮어씌워짐

연월일폴더

.mkdirs() 여러 개 디렉터리 만들어준다,,
밀리초까지 구분

UUID

파일명 앞에 UUID를 만들어 붙여줌. (해시 개념) -> 왜 쓰는지 이해하자~

  • UUID
    : 범용 고유 식별자(Universally Unique Identifier, UUID) 는 해당 타입의 다른 모든 리소스 중에서 리소스를 고유하게 식별하는 데 사용되는 레이블

UUID 생성법

java.util.UUID 클래스 사용

[JAVA] UUID사용하여 고유한 값 만들기

UUID 만들기

//UUID 생성		UUID-name.png (UUID는 겹치지 않는다고 보면됨.)
UUID uuid = UUID.randomUUID();
System.out.println(uuid); //42eae8e6-4731-42db-bab0-71d9ace5b28b 이런 식으로 나왔다
String newFileName = uuid.toString() + "-" + upFile.getOriginalFilename();


바보라니..

  • UUID 넣고 저장
@PostMapping("/file")
public String file(@RequestParam("file1") MultipartFile upFile) {
	System.out.println("파일 이름 : " +upFile.getOriginalFilename());
	System.out.println("파일 사이즈 : " +upFile.getSize());
	System.out.println("파일 타입 : " +upFile.getContentType());
	
	//UUID 생성		UUID-name.png (UUID는 겹치지 않는다고 보면됨.)
	UUID uuid = UUID.randomUUID();
	System.out.println(uuid); //42eae8e6-4731-42db-bab0-71d9ace5b28b 이런 식으로 나왔다
	String newFileName = uuid.toString() + "-" + upFile.getOriginalFilename();
	System.out.println("새로 만들어진 파일이름" + newFileName);
	
	//Real upload
	File f = new File("c:/temp/upfile", newFileName); //파일경로, 파일이름
	try {
		upFile.transferTo(f); //multipartfile의 메소드
	} catch (IllegalStateException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	return "redirect:/file";
}

Thumbnailator

자바는 원래 image.io ??에서 썸네일 만드는 기능이 있다. 하지만 별로니까 딴거쓰자

pom.xml

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

FileController.java
.transferTo() 되고 나면,,,

FileOutputStream thumbnail = new FileOutputStream(new File("c:/temp/upfile", "s_"+newFileName)); //썸네일은 이름 앞에 s_를 붙인다.

FileOutputStream이 만들어진다.
이름 앞에 붙는 s_는 이름 똑같으면 덮어씌워지니까 한다

//썸네일 만들기
FileOutputStream thumbnail = 
		new FileOutputStream(new File("c:/temp/upfile", "s_"+newFileName)); //썸네일은 이름 앞에 s_를 붙인다.
Thumbnailator.createThumbnail(upFile.getInputStream(), thumbnail, 100, 100); //inputstream(실제 올라갈 파일내용물), 저장경로, 파일크기 x, y
thumbnail.close();

테스트는 좀 큰 파일로
(gif했는데)

java.lang.IllegalStateException: File has been moved - cannot be read again

upFile.transferTo(f); 썸네일 만들기 전에 했는데 다음으로 내림. (책은 전인데 다음으로 바꿈)

@PostMapping("/file")
public String file(@RequestParam("file1") MultipartFile upFile) {
	System.out.println("파일 이름 : " +upFile.getOriginalFilename());
	System.out.println("파일 사이즈 : " +upFile.getSize());
	System.out.println("파일 타입 : " +upFile.getContentType());
	
	//UUID 생성		UUID-name.png (UUID는 겹치지 않는다고 보면됨.)
	UUID uuid = UUID.randomUUID();
	System.out.println(uuid); //42eae8e6-4731-42db-bab0-71d9ace5b28b 이런 식으로 나왔다
	String newFileName = uuid.toString() + "-" + upFile.getOriginalFilename();
	System.out.println("새로 만들어진 파일이름: " + newFileName);
	
	//Real upload
	File f = new File("c:/temp/upfile", newFileName); //파일경로, 파일이름
	try {

		//썸네일 만들기
		FileOutputStream thumbnail = 
				new FileOutputStream(new File("c:/temp/upfile", "s_"+newFileName)); //썸네일은 이름 앞에 s_를 붙인다.
		Thumbnailator.createThumbnail(upFile.getInputStream(), thumbnail, 100, 100); //inputstream(실제 올라갈 파일내용물), 저장경로, 파일크기 x, y
		thumbnail.close();
		
		upFile.transferTo(f); //multipartfile의 메소드
		
	} catch (IllegalStateException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	return "redirect:/file";
}

프로젝트 resources폴더에 upfile이라는 폴더를 만들어준다.

servlet-context.xml에서 매핑해주자

<resources mapping="/upfile/**" location="/resources/upfile/" />

경로를 써줄건데,,
윈도우는 최상위폴더 C:/지만
앞으로 저장할 리눅스, 유닉스는 /가 최상위폴더...

톰캣 가상으로 돌리고 있어서 이렇게 됐다.

실제 서버에 올라가는 경로랑 다르다.
root와 upfile이 실제 서버에 올라가는 경로.

@PostMapping("/file")
public String file(@RequestParam("file1") MultipartFile upFile, HttpServletRequest request) {
	System.out.println("파일 이름 : " +upFile.getOriginalFilename());
	System.out.println("파일 사이즈 : " +upFile.getSize());
	System.out.println("파일 타입 : " +upFile.getContentType());
	
	//경로
	String root = request.getSession().getServletContext().getRealPath("/"); //리눅스, 유닉스 서버는 c:/가 아님. 최상위는 /
	System.out.println("upfileURL : " + root); //upfileURL : C:\workspace-spring\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\feb15\
	String upfile = root + "resources\\upfile\\";
	System.out.println("upfile : " + upfile);
	
	//UUID 생성		UUID-name.png (UUID는 겹치지 않는다고 보면됨.)
	UUID uuid = UUID.randomUUID();
	System.out.println(uuid); //42eae8e6-4731-42db-bab0-71d9ace5b28b 이런 식으로 나왔다
	String newFileName = uuid.toString() + "-" + upFile.getOriginalFilename();
	System.out.println("새로 만들어진 파일이름: " + newFileName);
	
	//Real upload
	File f = new File(upfile, newFileName); //파일경로, 파일이름
	try {

		//썸네일 만들기
		FileOutputStream thumbnail = 
				new FileOutputStream(new File(upfile, "s_"+newFileName)); //썸네일은 이름 앞에 s_를 붙인다.
		Thumbnailator.createThumbnail(upFile.getInputStream(), thumbnail, 100, 100); //inputstream(실제 올라갈 파일내용물), 저장경로, 파일크기 x, y
		thumbnail.close();
		
		upFile.transferTo(f); //multipartfile의 메소드
		
	} catch (IllegalStateException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	return "redirect:/file";
}

갤러리 / 파일업로드

GalleryDTO를 만들고 DB에 gallery 테이블도 새로 생성.

DTO 칼럼명과 일치시켜뒀다.

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GalleryDTO {
	
	private int gno, glike, gdel;
	private String gtitle, gcontent, gfile, gdate, mname, mid;
	
}

파일업로드 기능 FileController에서 Util로 옮겨준다. (아직안함)

@PostMapping("/galleryInsert") // 갤러리 들어가기 글쓰기 저장하기 불러오기..?
public String galleryInsert(GalleryDTO dto) {
	System.out.println("제목: " +dto.getGtitle());
	System.out.println("내용~~~~: " + dto.getGcontent());
	
	//int result = galleryService.galleryInsert(dto);
	return "redirect:/gallery";
}

controller에서 dto로 받았다고해서
file까지 gfile로 하면 안된다
MultipartFile로 따로 받는다

<form action="./galleryInsert" method="post" enctype="multipart/form-data" onsubmit="fileCheck()">
	<div class="input-group mb-3">
		<span class="input-group-text" id="basic-addon3">title</span>
		<input type="text" name="gtitle" class="form-control" id="basic-url" aria-describedby="basic-addon3">
	</div>
	<div class="input-group mb-3" style="height: 300px;">
		<span class="input-group-text">content</span>
		<textarea class="form-control" aria-label="With textarea" name="gcontent"></textarea>
	</div>
	<div class="input-group mb-3">
		<input type="file" accept="image/*" name="file1" class="form-control" id="file1" aria-describedby="inputGroupFileAddon04" aria-label="Upload">
	</div>
	<div class="d-flex justify-content-end">
		<button class="btn btn-outline-warning" type="submit">Upload</button>
	</div>
</form>

file input의 name은 gfile로 해봤자,, MultipartFile로 해야해서 file1로 둔다.

@PostMapping("/galleryInsert") // 갤러리 들어가기 글쓰기 저장하기 불러오기..?
public String galleryInsert(GalleryDTO dto, @RequestParam("file1") MultipartFile upFile) {
	System.out.println("제목: " +dto.getGtitle());
	System.out.println("내용~~~~: " + dto.getGcontent());
	System.out.println("파일명: " + upFile.getOriginalFilename()); //실제 업로드할때 파일이름이고
	//보낼건 위 3개와 세션(서비스에서 넣어줌.)
	dto.setGfile(upFile.getOriginalFilename()); //세팅할땐 UUID 넣은 newFileName
	
	int result = galleryService.galleryInsert(dto);
	return "redirect:/gallery";
}

GalleryController.java
파일 업로드 메서드
Util에 만들어서 쓰기

@PostMapping("/galleryInsert") // 갤러리 들어가기 글쓰기 저장하기 불러오기..?
public String galleryInsert(GalleryDTO dto, @RequestParam("file1") MultipartFile upFile) {
	System.out.println("제목: " +dto.getGtitle());
	System.out.println("내용~~~~: " + dto.getGcontent());
	System.out.println("파일명: " + upFile.getOriginalFilename()); //실제 업로드할때 파일이름이고
	//보낼건 위 3개와 세션(서비스에서 넣어줌.)
	
	//파일 업로드 -> util
	String newFileName = util.fileUpload(upFile);
	
	dto.setGfile(newFileName); //UUID 넣은 newFileName
	
	int result = galleryService.galleryInsert(dto);
	return "redirect:/gallery";
}

Util.java

// 조금 더 센스있다면 UUID 뽑는 것도 다른 util로 뺐을 것
public String fileUpload(MultipartFile upFile) {
	//경로 저장
	//root는 사실 필요없지만 아까 했으니까
	String root = getSession().getServletContext().getRealPath("/"); //리눅스, 유닉스 서버는 c:/가 아님. 최상위는 /
	String upfilePath = root + "resources\\upfile\\";
	
	//UUID 생성
	UUID uuid = UUID.randomUUID();
	//UUID를 포함한 파일명
	String newFileName = uuid.toString() + upFile.getOriginalFilename();
	
	//진짜 업로드
	File file = new File(upfilePath, newFileName); //파일경로, 파일이름
	try {
		//썸네일 만들기
		FileOutputStream thumbnail = new FileOutputStream(new File(upfilePath, "s_" + newFileName));
		Thumbnailator.createThumbnail(upFile.getInputStream(), thumbnail, 100, 100); //실제파일 inputstream으로 만들기, 썸네일 경로, 썸네일 사이즈
		thumbnail.close();
		
		upFile.transferTo(file); //자바 밖으로 가서 파일을 저장해온다.
	} catch (IllegalStateException | IOException e) { //멀티캐치
		e.printStackTrace();
	}
	
	return newFileName;
}

올라간 파일은

이거 우클릭 - Browse Deployment Location
누르면 가상? 폴더가 나오는데
프로젝트폴더/resources/upfile 경로에 업로드한 파일이 들어간다

가상이라 끄면 사라진다고 하셨나.. 아무튼 그렇다(가상 톰캣 폴더)

1개의 댓글

comment-user-thumbnail
2024년 2월 23일

코드 좀 훔쳐갑니다!
ㅎㅎㅎ

답글 달기