- 서블릿 프로그래밍
- 서블릿, 필터, 리스너 컴포넌트 만들고 배치하는 방법
- GET/POST/PUT 등 요청 메서드를 구분하는 방법
- 요청 파라미터 값을 꺼내는 방법
- multipart/form-data 로 전송된 파라미터 값을 다루는 방법
- 썸네일 이미지를 생성하는 방법
- HttpServlet 클래스의 역할 이해
Servlet 정리
servlet-app 프로젝트
ㄴ eomcs.servlet.ex03 패키지 생성
=>
ㄴ ㄴ /Users/사용자명/git/eomcs-java/eomcs-servlet/app/src/main/java/com/eomcs/web/ex03 에 존재하는 파일 모두 복사
=>
ㄴ eomcs.servlet.ex03 패키지에 복사한 파일 인텔리제이에서 붙여넣기
=>
클라이언트로 출력하기
출력 스트림을 꺼내기 전에
출력할 때 사용할 문자표(charset)를 지정하지 않으면
리턴 받은 출력 스트림은 기본 문자표 ISO-8859-1 을 사용한다.
즉 자바의 유니코드 문자를 ISO-8859-1 문자표에 따라 변환하여 출력한다.
자바(Unicode2;UTF-16) ===> 출력문자(ISO-8859-1)
Servlet01.java (ex03)
// 클라이언트로 출력하기
package eomcs.servlet.ex03;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex03/s1") // http://localhost:8080/web/ex03/s1
public class Servlet01 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
PrintWriter out = res.getWriter();
// 다음 영어 유니코드 문자는 ISO-8859-1 문자표에 있기 때문에 제대로 변환된다.
out.println("Hello!");
// 그러나 다음 유니코드 문자는 ISO-8859-1 문자표에 없기 때문에
// 없다는 의미에서 '?' 문자표 바뀌어 출력된다.
out.println("안녕하세요!");
out.println("こんにちは");
out.println("您好");
out.println("مع السلامة؛ إلى اللقاء!");
}
}
=>
ㄴ App 실행
=>
ㄴ 다음 유니코드 문자는 ISO-8859-1 문자표에 없기 때문에 없다는 의미에서 '?' 문자표 바뀌어 출력됨
클라이언트로 출력하기 - 한글 깨짐 현상 처리
한글 깨짐 처리하기
=> 출력 스트림을 꺼내기 전에
출력 스트림이 사용할 문자표(charset)를 지정하라.
=> 반드시 출력 스트림을 얻기 전에 설정해야 한다.
res.setContentType("MIME Type;charset=문자표이름");
MIME Type : Multi-purpose Internet Mail Extension
=> 콘텐트의 형식을 표현
=> 콘텐트타입/상세타입
=> 예) text/plain, text/css, text/html 등
=> 웹 브라우저는 콘텐트를 출력할 때 서버가 알려준 MIME 타입을 보고
어떤 방식으로 출력할 지 결정한다.
Servlet02.java (ex03)
// 클라이언트로 출력하기 - 한글 깨짐 현상 처리
package eomcs.servlet.ex03;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex03/s2")
public class Servlet02 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.setContentType("text/plain;charset=UTF-8"); // UCS2(UTF-16) ==> UTF-8
PrintWriter out = res.getWriter();
out.println("Hello!");
// 한글이나 아랍문자, 중국문자, 일본문자는
// UTF-8 문자표에 정의되어 있기 때문에
// UTF-8 문자로 변환할 수 있다.
out.println("안녕하세요!");
out.println("こんにちは");
out.println("您好");
out.println("مع السلامة؛ إلى اللقاء!");
}
}
=>
ㄴ 정상적으로 출력됨을 확인
클라이언트로 출력하기 - HTML 출력하기
Servlet03.java (ex03)
// 클라이언트로 출력하기 - HTML 출력하기
package eomcs.servlet.ex03;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex03/s3")
public class Servlet03 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// HTML 출력할 때 MIME 타입에 HTML을 지정하지 않으면
// 웹 브라우저는 일반 텍스트로 간주하여 출력한다.
res.setContentType("text/html;charset=UTF-8"); // UTF-16 ==> UTF-8
PrintWriter out = res.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head><title>servlet03</title></head>");
out.println("<body><h1>안녕하세요2</h1></body>");
out.println("</html>");
}
}
Servlet03.java (ex03)
ㄴ HTML 출력 시 MIME 타입에 HTML 이 아닌 plain 을 지정
ㄴ build 해주기
=>
ㄴ html 이 아닌 날것의 텍스트가 출력됨
Servlet03.java (ex03)
ㄴ text 를 toxt 라고 오타를 내도록 함
=>
ㄴ 파일 변경 시 Gradle 탭의 servlet-app > Tasks > build 대신 servlet-app > Tasks > classes 를 이용하여 컴파일하도록 함
=>
=>
ㄴ s3 라는 파일이 다운로드 되는 현상이 발생
저장 시 자동 빌드 설정
=>
ㄴ Tools > Actions on Save 에서 Build project 체크 > [OK] 선택
ㄴ Optimize imports, Rearrange code 도 추가로 설정해주기
ㄴ servlet-app/app/src/main/webapp 바로 아래에 photo.jpeg 라는 이름의 이미지 파일이 있는지 확인
=>
클라이언트로 출력하기 - 바이너리 데이터 출력하기
Servlet04.java (ex03)
// 클라이언트로 출력하기 - 바이너리 데이터 출력하기
package eomcs.servlet.ex03;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
@WebServlet("/ex03/s4")
public class Servlet04 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// photo.jpeg 파일의 실제 경로 알아내기
// 1) 서블릿의 환경 정보를 다루는 객체를 먼저 얻는다.
ServletContext ctx = req.getServletContext();
// 2) ServletContext를 통해 웹 자원의 실제 경로를 알아낸다.
// => getRealPath(현재 웹 애플리케이션의 파일 경로) : 실제 전체 경로를 리턴한다.
String path = ctx.getRealPath("/photo.jpeg");
System.out.println(path);
FileInputStream in = new FileInputStream(path);
// 바이너리를 출력할 때 MIME 타입을 지정해야 웹 브라우저가 제대로 출력할 수 있다.
// => 웹 브라우저가 모르는 형식을 지정하면 웹 브라우저는 처리하지 못하기 때문에
// 그냥 다운로드 대화상자를 띄운다.
res.setContentType("image/jpeg");
OutputStream out = res.getOutputStream(); // 바이너리타입
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
out.flush(); // 버퍼 데코레이터에 보관된 데이터를 클라이언트로 방출한다.
out.close();
in.close();
}
}
ㄴ 바이너리타입 데이터를 출력할 때 OutputStream 을 이용
=>
ㄴ flush() 중요!
ㄴ 바이너리를 출력할 때 MIME 타입을 image/jpeg 로 지정
=>
ㄴ Content-Type 이 image/jpeg 임을 확인
=>
Servlet03.java (ex03)
ㄴ HTML 은 PrintWriter를 이용하여 HTML 코드를 출력하는 것이 가능
ㄴ ex04 패키지를 만들고 ex03 패키지와 동일한 방법으로 파일 가져와서 붙여넣기
ㄴ [name:] 우클릭 > Disable Hints 선택하여 힌트 없애기
클라이언트가 보낸 데이터 읽기 - GET 요청 데이터 읽기
GET 요청
- 웹 브라우저에 URL을 입력한 후 엔터를 치면 GET 요청을 보낸다.
- 웹 페이지에서 링크를 클릭하면(자바스크립트 처리하지 않은 상태) GET 요청을 보낸다.
- 웹 페이지의 폼(method='GET' 일 때)에서 전송 버튼을 클릭하면 GET 요청을 보낸다.
테스트
- http://localhost:8080/web/ex04/test01.html 실행
test01.html (ex04)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test01</title>
</head>
<body>
<h1>GET 요청1</h1>
<p>
웹 브라우저 주소 창에 'http://localhost:8888/ex04/s1?name=홍길동&age=20'
입력한 후 엔터를 친다.
</p>
<h1>GET 요청2</h1>
<p>
다음 링크를 클릭한다.<br>
<a href="http://localhost:8888/ex04/s1?name=홍길동&age=20">GET 요청</a>
</p>
<h1>GET 요청3</h1>
<p>
다음 입력폼에 값을 넣고 '전송' 버튼을 클릭한다.
</p>
<form action="http://localhost:8888/ex04/s1" method="get">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
<input type="submit" value="전송">
</form>
</body>
</html>
웹브라우저에서 웹서버의 자원을 요청하는 방법
1) 서블릿 클래스를 실행하고 싶을 때
=> 서블릿 클래스의 실제 위치:
톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/classes/com/eomcs/web/ex04/Servlet01.class
=> 요청:
해당 서블릿을 서버에 등록할 때 사용한 URL을 지정해야 한다.
http://localhost:9999/eomcs-java-web/ex04/s1
2) HTML, CSS, JavaScript, JPEG 등 정적 파일을 받고 싶을 때
=> 정적 파일의 실제 위치:
톰캣배치폴더/wtpwebapps/eomcs-java-web/ex04/test01.html
=> 요청:
http://localhost:9999/eomcs-java-web/ex04/test01.html
3) /WEB-INF/ 폴더에 있는 정적 파일을 받고 싶을 때
=> 정적 파일의 실제 위치:
톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/ex04/test01.html
=> 요청:
/WEB-INF 폴더 아래에 있는 파일은 클라이언트에서 요청할 수 없다!
웹 애플리케이션의 정보를 두는 폴더이기 때문이다.
HTTP 요청 형식
method sp request-URI sp http_version CRLF
*((general header | request header | entity header) CRLF)
CRLF // 헤더와 메시지 본문을 구분하기 위한 빈 줄
message-body
GET 요청 HTTP 프로토콜 예)
=> GET 요청은 데이터를 request-URI에 붙여서 보낸다.
=> request-URI
예) /java-web/ex04/s1?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
서블릿 URL : /java-web/ex04/s1
데이터(Query String) : ?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
=> 데이터 형식
이름=값&이름=값&이름=값
=> request-URI는 URI 규칙에 따라 인코딩되어야 한다.
URL 인코딩(=퍼센트 인코딩)
- URI 작성 규칙에 따라 데이터를 변환하는 것.
- 문법
원래 코드를 4비트 단위로 짤라서 문자로 간주하고 문자 코드로 인코딩 한다.
URI 작성 규칙
- RFC 3986 규약에 따라 URL을 작성해야 한다.
- URL 비예약어(unreserved)인 경우 그대로 사용할 수 있다.
예) 영어 대.소문자, -, _, ., ~
- URL 문법에서 사용하기 위해 예약된 키워드 및 기타 언어의 문자를 데이터로 작성할 때는 URL 인코딩해야 한다.
예약어 => !, *, : , ; ,#, /, @ 등
기타 문자 => 한글, 한자, 일본어 등
즉 8비트 값이 음수일 경우 URL 인코딩 대상이 된다.
톰캣 서버에 GET 요청으로 받은 데이터가 UTF-8 임을 설정하는 방법
톰캣 7 이하 버전에서는 conf/server.xml 파일에서 다음과 같이 설정해야 한다.
<Connector connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"
URIEncoding="UTF-8" <------ 이 속성을 추가하라!
/>
주의!
=> 톰캣 서버가 아닌 다른 서블릿 컨테이너는 사용 안내서를 확인해보라!
HTTP GET 요청 예)
------------------------------------------------------------------------------
GET /java-web/ex04/s1?name=AB%EA%B0%80%EA%B0%81&age=20 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, likeGecko)
Chrome/73.0.3683.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6
빈 줄
HTTP 응답 프로토콜
=> 형식
status-line(HTTP프로토콜 상태코드 간단한문구) CRLF
*(general header | response header | entity header) CRLF
CRLF
message-body
=> 예:
HTTP 응답 예)
------------------------------------------------------------------------------
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 27
Date: Thu, 28 Mar 2019 05:46:08 GMT
<== 빈 줄
이름=홍길동
나이=20
URI (Uniform Resource Identifier)
=> 웹 자원의 위치를 가리키는 식별자
=> 종류
URL(Uniform Resource Locator)
scheme:[//[user:password@]host[:port]][/]path[?query string][#fragment]
예) http://localhost:8080/ex04/s1?name=홍길동&age=20
URN(Uniform Resource Name)
<URN> ::= "urn:" <NID> ":" <NSS>
예) urn:lex:eu:council:directive:2010-03-09;2010-19-UE
ㄴ URI : Uniform Resource Indentifier
Servlet01.java (ex04)
// 클라이언트가 보낸 데이터 읽기 - GET 요청 데이터 읽기
package eomcs.servlet.ex04;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex04/s1")
public class Servlet01 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// 웹 브라우저가 보낸 데이터 읽기
// ServletRequest.getParameter("파라미터이름")
//
String name = req.getParameter("name");
String age = req.getParameter("age");
res.setContentType("text/plain;charset=UTF-8");
PrintWriter out = res.getWriter();
out.printf("이름=%s\n", name);
out.printf("나이=%s\n", age);
}
}
=>
ㄴ http://localhost:8888/ex04/s1 요청 결과
=>
ㄴ http://localhost:8888/ex04/s1?age=20 요청 결과
=>
ㄴ http://localhost:8888/ex04/s1?age=20&name=ok 요청 결과
=>
ㄴ http://localhost:8888/ex04/s1?age=20&name=한글 요청 결과
클라이언트가 보낸 데이터 읽기 - POST 요청 데이터 읽기
HTTP 요청 형식
method sp request-URI sp http_version CRLF
*((general header | request header | entity header) CRLF)
CRLF
message-body
POST 요청 HTTP 프로토콜 예)
=> POST 요청은 데이터를 message-body에 붙여서 보낸다.
=> 데이터 형식과 URL 인코딩은 GET 요청과 같다.
=> 예)
POST /java-web/ex04/s2 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 33
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:8080
Upgrade-Insecure-Requests:1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel MacOS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/73.0.3683.86 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,
Referer:http://localhost:8080/java-web/ex04/test02.html
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6
빈 줄
name=AB%EA%B0%80%EA%B0%81&age=20
GET 요청 vs POST 요청
0) 데이터 전송 방식
=> GET
- request uri(URL 주소)에 데이터를 포함한다.
예) /java-web/ex04/s1?name=AB%EA%B0%80%EA%B0%81&age=20
=> POST
- message body에 데이터를 포함한다.
예) name=AB%EA%B0%80%EA%B0%81&age=20
1) 전송 데이터 용량
=> GET
- 대부분의 웹서버가 request-line과 헤더의 크기를 8KB로 제한하고 있다.
- 따라서 긴 게시글과 같은 큰 용량의 데이터를 GET 방식으로 전송할 수 없다.
=> POST
- HTTP 요청 헤더 다음에 message-body 부분에 데이터를 두기 때문에
용량의 제한 없이 웹 서버에 전송할 수 있다.
- 즉 웹 서버가 제한하지 않는 한 전송 데이터의 크기에 제한이 없다.
- 웹 서버가 제한한다?
특정 사이트에서는 게시글의 크기나 첨부파일의 크기에 제한을 둔다.
=> 용도
- 게시글 조회나 검색어 입력 같은 간단한 데이터 전송에는 GET 요청으로 보내고
- 게시글 등록이나 첨부파일 같은 큰 데이터 전송에는 POST 요청으로 보낸다.
2) 바이너리 데이터 전송
=> GET
- request-URI가 텍스트로 되어 있다.
따라서 바이너리 데이터를 request-URI에 붙여서 전송할 수 없다.
- 그럼에도 꼭 GET 요청으로 바이너리 데이터를 보내고자 한다면?
바이너리 데이터를 텍스트로 변환하면 된다.
예를 들어 바이너리 데이터를 Base64로 인코딩하여 텍스트를 만든 후에
GET 요청 방식대로 이름=값 으로 보내면 된다.
- 그래도 결국 용량 제한 때문에 바이너리 데이터를 GET 요청으로 전송하는 것은 바람직하지 않다.
=> POST
- 이 방식에서도 이름=값 형태로는 바이너리 값을 전송할 수 없다.
- multipart 형식을 사용하면 바이너리 데이터를 보낼 수 있다.
- 보통 파일 업로드를 구현할 때 이 multipart 전송 방식으로 사용한다.
3) 보안
=> GET
- URL에 전송 데이터가 포함되어 있기 때문에
사용자 아이디나 암호 같은 데이터를 GET 방식으로 전송하는 것은 위험하다.
- 웹 브라우저는 주소 창에 입력한 값을 내부 캐시에 보관해 두기 때문이다.
- 그러나 게시물 번호 같은 데이터는 URL에 포함되어야 한다.
그래야 다른 사람에게 URL과 함께 데이터를 보낼 수 있다.
=> POST
- mesage-body 부분에 데이터가 있기 때문에
웹 브라우저는 캐시에 보관하지 않는다.
- 또한 주소 창에도 보이지 않는다.
- 사용자 암호 같은 데이터를 전송할 때는 특히 이 방식으로 보내는 것이 바람직 하다.
즉 보내는 데이터를 웹 브라우저의 캐시 메모리에 남기고 싶지 않을 때는 POST 방식을 사용한다.
- 꺼꾸로 특정 페이지를 조회하는 URL일 경우 POST 방식을 사용하면
URL에 조회하려는 정보의 번호나 키를 포함할 수 없기 때문에
이런 상황에서는 POST 방식이 적절하지 않다.
오히려 GET 방식이 적합하다.
test02.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test02</title>
</head>
<body>
<h1>POST 요청</h1>
<p>
다음 입력폼에 값을 넣고 '전송' 버튼을 클릭한다.
</p>
<!-- action 속성의 URL이 상대 경로라면,
현재 페이지의 경로를 기준으로 URL을 계산한다.-->
<form action="s2" method="post">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
<input type="submit" value="전송">
</form>
</body>
</html>
Servlet02.java (ex04)
// 클라이언트가 보낸 데이터 읽기 - POST 요청 데이터 읽기
package eomcs.servlet.ex04;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex04/s2")
public class Servlet02 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// POST 요청
// - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
//
// 테스트
// - http://localhost:8080/web/ex04/test02.html 실행
//
// 웹 브라우저가 보낸 데이터 읽기
// => 데이터를 읽을 때는 GET 요청과 POST 요청이 같다.
// ServletRequest.getParameter("파라미터이름")
//
// => POST 요청으로 보낸 데이터는 서블릿 컨테이너 측에서 영어(ISO-8859-1)라고 간주한다.
// 그래서 한글 코드 값도 영어라고 간주하고 UCS2(UTF-16) 문자 코드로 변환하기 때문에
// getParameter() 가 리턴한 한글은 깨진 한글이다.
//
// 클라이언트가 보낸 한글을 읽을 때 깨지는 문제 해결?
// => 다음 코드의 주석을 풀고 테스트 해보라!
// 정상적으로 잘 출력될 것이다.
req.setCharacterEncoding("UTF-8");
// => 원리
// getParameter()를 최초로 호출하기 전에 먼저
// 클라이언트 보낸 데이터의 인코딩 형식이 어떤 문자표로 되어 있는지 알려줘야 한다.
// => 주의!
// 반드시 getParamter()를 최초로 호출하기 전이어야 한다.
// 한 번 getParameter()를 호출한 후에는 소용없다.
//
String age = req.getParameter("age");
String name = req.getParameter("name");
res.setContentType("text/plain;charset=UTF-8");
PrintWriter out = res.getWriter();
out.printf("이름=%s\n", name);
out.printf("나이=%s\n", age);
out.println("-------------------");
char[] chars = name.toCharArray();
for (char c : chars) {
out.printf("%04x\n", (int) c);
}
}
}
Postman 이용해 보기
ㄴ servlet-app 컬렉션의 더보기 아이콘 > Add request 선택
=>
ㄴ ex04/s2 로 새로운 request 이름 설정
=>
ㄴ localhost:8888/ex04/s2 로 경로 설정
=>
ㄴ POST 로 설정해주기
=>
ㄴ Body 탭에서 x-www-form-urlencoded 선택
=>
ㄴ name : 홍길동, age : 20 으로 설정해주기
ㄴ [Send] 선택
=>
=>
Servlet02.java (ex04)
ㄴ 주석처리 해보기
=>
ㄴ 이름이 정상적으로 출력되지 않음을 확인
=>
Servlet02.java (ex04)
ㄴ 다시 주석 풀어주기
=>
ㄴ 이름이 정상적으로 출력됨을 확인
=>
ㄴ 해당 값 입력
=>
ㄴ 정상적으로 출력됨을 확인
ㄴ 점선 아래는 해당 값들을 16진수로 표현하고 4자리로 정렬한 값
=>
Servlet02.java (ex04)
ㄴ 다시 주석 처리 해주기
=>
ㄴ 이름이 정상적으로 출력되지 않음을 확인
=>
클라이언트가 보낸 데이터 읽기 - 파일 업로드 처리하기
Servlet03.java (ex04)
// 클라이언트가 보낸 데이터 읽기 - 파일 업로드 처리하기
package eomcs.servlet.ex04;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet("/ex04/s3")
public class Servlet03 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// POST 요청으로 파일 전송하기
// - 파일을 첨부하여 서버에 전송한다.
// - multipart/form-data 형식으로 데이터를 전송하지 않으면
// 첨부 파일의 데이터는 받을 수 없다.
//
// 테스트
// - http://localhost:8080/java-web/ex04/test03.html 실행
//
req.setCharacterEncoding("UTF-8");
String age = req.getParameter("age");
String name = req.getParameter("name");
String photo = req.getParameter("photo");
res.setContentType("text/plain;charset=UTF-8");
PrintWriter out = res.getWriter();
out.printf("이름=%s\n", name);
out.printf("나이=%s\n", age);
out.printf("사진=%s\n", photo);
// GET 요청이나 일반 POST 요청을 한 경우에는
// 파일이 이름만 넘어오고 파일 데이터는 넘어오지 않는다.
//
// 파일의 데이터를 전송하려면,
// <form> 태그에 enctype 속성을 "multipart/form-data"로 설정해야 한다.
//
// 단 멀티파트 형식으로 데이터가 넘어온 경우에는
// getParameter()로 그 값을 꺼낼 수 없다.
}
}
test03.html (ex04)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test03</title>
</head>
<body>
<h1>파일 업로드</h1>
<p>
'파일 선택(Browse)' 버튼을 클릭하여 사진 파일을 선택한 후 '전송' 버튼을 누른다.
</p>
<h2>GET 요청으로 파일 업로드</h2>
<form action="s3" method="get">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
사진: <input type="file" name="photo"><br>
<input type="submit" value="GET 전송">
</form>
<p>
GET 으로 파일을 전송할 수 없다. 단지 파일 이름만 전송한다.
</p>
<h2>POST 요청으로 파일 업로드</h2>
<form action="s3" method="post">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
사진: <input type="file" name="photo"><br>
<input type="submit" value="POST 전송">
</form>
<p>
현재 form의 데이터 인코딩 형식은 "application/x-www-form-urlencoded"이다.
즉 "이름=값&이름=값&이름=값" 형식으로 서버에 데이터를 보낸다.
이 방식 또한 첨부한 파일의 데이터를 보내지 못한다.
단지 파일 이름만 보낸다.
</p>
<h2>POST 요청 + multipart/form-data 형식으로 파일 업로드</h2>
<form action="s3" enctype="multipart/form-data" method="post">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
사진: <input type="file" name="photo"><br>
<input type="submit" value="POST 전송">
</form>
<p>
파일을 업로드 하려면, 반드시 multipart/form-data 형식으로 POST 요청해야 한다.
그래야 파일의 이름 뿐만아니라 파일 데이터도 전송한다.
</p>
</body>
</html>
=>
=>
ㄴ Get 방식으로 요청되어 Headers 에서 전달된 값 확인 가능
=>
=>
=>
ㄴ POST 방식으로 요청되어 Payload 에서 전달된 값 확인 가능
=>
ㄴ Content-type 이 application/x-www-form-urlencoded 로 설정되어 있음을 확인
=>
=>
=>
ㄴ POST 방식으로 요청되어 Payload 에서 전달된 값 확인 가능
ㄴ photo 는 바이너리 데이터
=>
ㄴ view source 선택하여 확인
멀티파트 파일 업로드 처리하기 - Servlet 3.0의 기본 라이브러리 사용
멀티파트 형식의 데이터를 처리할 서블릿으로 선언하라.
1) DD 파일(web.xml)에 설정하기
<servlet>
<servlet-name>ex04.Servlet05</servlet-name>
<servlet-class>com.eomcs.web.ex04.Servlet05</servlet-class>
<multipart-config>
<max-file-size>10000000</max-file-size>
</multipart-config>
</servlet>
2) 애노테이션으로 설정하기
test06.html (ex04)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test05</title>
</head>
<body>
<h1>POST 요청 - 파일 업로드(multipart/form-data)</h1>
<p>
첨부 파일의 데이터를 서버에 보내려면 "multipart/form-data" 형식으로 전송해야 한다.
form 태그의 enctype 속성을 "multipart/form-data" 로 변경하라.
</p>
현재 HTML의 URL을 기준으로 요청 URL을 상대적으로 기술할 수 있다.
<form action="s5"
method="post"
enctype="multipart/form-data">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
사진: <input type="file" name="photo"><br>
<input type="submit" value="전송">
</form>
</body>
</html>
Servlet05.java (ex04)
// 멀티파트 파일 업로드 처리하기 - Servlet 3.0의 기본 라이브러리 사용
package eomcs.servlet.ex04;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
// 2) 애노테이션으로 설정하기
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/ex04/s5")
public class Servlet05 extends GenericServlet {
private static final long serialVersionUID = 1L;
private String uploadDir;
@Override
public void init() throws ServletException {
this.uploadDir = this.getServletContext().getRealPath("/upload");
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// 테스트
// - http://localhost:8080/java-web/ex04/test05.html 실행
//
// Servlet 3.0의 멀티파트 처리 기능을 이용할 때는
// 원래 하던대로 클라이언트가 보낸 데이터의 인코딩을 지정하라.
req.setCharacterEncoding("UTF-8");
// 파라미터로 받은 ServletRequest를 원래의 타입으로 변환하라.
HttpServletRequest httpReq = (HttpServletRequest) req;
res.setContentType("text/html;charset=UTF-8");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head><title>servlet04</title></head>");
out.println("<body><h1>파일 업로드 결과</h1>");
// 일반 폼 데이터를 꺼낼 때는 원래 하던 방식대로 값을 꺼낸다.
out.printf("이름=%s<br>\n", httpReq.getParameter("name"));
out.printf("나이=%s<br>\n", httpReq.getParameter("age"));
// 파일 데이터는 getPart()를 이용한다.
Part photoPart = httpReq.getPart("photo");
if (photoPart.getSize() > 0) {
// 파일을 선택해서 업로드 했다면,
String filename = UUID.randomUUID().toString();
photoPart.write(this.uploadDir + "/" + filename);
out.printf("사진=%s<br>\n", filename);
out.printf("<img src='../upload/%s' height='50'><br>\n", filename);
out.printf("<img src='../upload/%s'><br>\n", filename);
}
out.println("</body></html>");
}
}
=>
=>
=>
클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우I
test06.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test06</title>
</head>
<body>
<h1>같은 이름으로 여러 개의 데이터 보내기</h1>
<h2>좋아하는 영화 장르</h2>
<form action="s6" method="get">
<input name="genre1" type="checkbox">로맨틱<br>
<input name="genre2" type="checkbox">스릴러<br>
<input name="genre3" type="checkbox">호러<br>
<input name="genre4" type="checkbox">드라마<br>
<input name="genre5" type="checkbox">액션<br>
<input name="genre6" type="checkbox">SF<br>
<input type="submit" value="전송">
</form>
</body>
</html>
Servlet05.java (ex04)
// 클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우
package eomcs.servlet.ex04;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet("/ex04/s6")
public class Servlet06 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// POST 요청
// - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
//
// 테스트
// - http://localhost:8080/java-web/ex04/test06.html 실행
//
// 1) 서로 다른 이름으로 값을 보낼 경우
// => 예) genre1=on&genre2=on&genre4=on
// => 다음과 같이 각각의 이름에 대해 값을 꺼내 확인해야 한다.
String genre1 = req.getParameter("genre1");
String genre2 = req.getParameter("genre2");
String genre3 = req.getParameter("genre3");
String genre4 = req.getParameter("genre4");
String genre5 = req.getParameter("genre5");
String genre6 = req.getParameter("genre6");
res.setContentType("text/plain;charset=UTF-8");
PrintWriter out = res.getWriter();
out.println("선택한 장르:");
if (genre1 != null) {
out.println("로맨틱");
}
if (genre2 != null) {
out.println("스릴러");
}
if (genre3 != null) {
out.println("호러");
}
if (genre4 != null) {
out.println("드라마");
}
if (genre5 != null) {
out.println("액션");
}
if (genre6 != null) {
out.println("SF");
}
}
}
=>
=>
=>
ㄴ 체크박스를 체크한 값이 on 으로 전달됨
=>
test06.html
ㄴ checkbox 타입의 input 태그의 value 값을 지정해주기 (on 대신 출력할 값)
=>
=>
ㄴ on 대신 input 태그의 value 값이 url 에 전달됨
클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우II
test06_2.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test06</title>
</head>
<body>
<h1>같은 이름으로 여러 개의 데이터 보내기 II</h1>
<h2>좋아하는 영화 장르</h2>
<form action="s6_2" method="get">
<input type="checkbox" name="genre" value="1">로맨틱<br>
<input type="checkbox" name="genre" value="2">스릴러<br>
<input type="checkbox" name="genre" value="3">호러<br>
<input type="checkbox" name="genre" value="4">드라마<br>
<input type="checkbox" name="genre" value="5">액션<br>
<input type="checkbox" name="genre" value="6">SF<br>
<input type="submit" value="전송">
</form>
</body>
</html>
ㄴ value 값을 숫자 1 ~ 6 으로 주기
Servlet06_2.java
// 클라이언트가 보낸 데이터 읽기 - 여러 개의 데이터를 같은 이름으로 보낸 경우
package eomcs.servlet.ex04;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/ex04/s6_2")
public class Servlet06_2 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// POST 요청
// - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
//
// 테스트
// - http://localhost:8080/java-web/ex04/test06.html 실행
//
// 2) 같은 이름으로 값을 보낼 경우
// => 예) genre=1&genre=2&genre=4
// => 다음과 같이 한 번에 값을 리턴 받는다.
String[] genres = req.getParameterValues("genre");
String[] genreData = {"", "로맨틱", "스릴러", "호러", "드라마", "액션", "SF"};
res.setContentType("text/plain;charset=UTF-8");
PrintWriter out = res.getWriter();
out.println("선택한 장르:");
for (String genre : genres) {
out.println(genreData[Integer.parseInt(genre)]);
}
// 같은 값을 여러 개 입력 받아야 하는 경우
// 같은 이름을 사용하라.
// 그러면 위와 같이 한 번에 배열로 그 값들을 받을 수 있다.
// 배열로 받으면 반복문을 이용하여 보다 쉽고 간결하게 처리할 수 있다.
}
}
=>
=>
=>
ㄴ 아무것도 체크하지 않고 [전송] 선택 해보기
=>
ㄴ 아예 값이 넘어가지 않음을 확인
=>
Servlet06_2.java
ㄴ genres 를 출력하여 넘어가는 값이 null 임을 확인 => 체크박스를 선택하지 않으면 어떠한 값도 넘어가지 않음을 확인할 수 있음
클라이언트가 보낸 데이터 읽기 - 빈 값과 null
test07.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test07</title>
</head>
<body>
<h1>빈 값을 보내기</h1>
<form action="s7" method="get">
a: <input type="text" name="a" value="aaaa"><br>
b: <input type="text" name="b"><br>
a 입력 상자에는 값을 넣고, b 입력 상자에는 값을 입력하지 않고 전송해보자!<br>
c: <input type="checkbox" name="c"><br>
체크 상자를 체크한 경우와 체크하지 않은 경우를 확인해보자!<br>
<input type="submit" value="전송">
</form>
</body>
</html>
Servlet07.java
// 클라이언트가 보낸 데이터 읽기 - 빈 값과 null
package eomcs.servlet.ex04;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet("/ex04/s7")
public class Servlet07 extends GenericServlet {
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// POST 요청
// - 웹 페이지의 폼(method='POST' 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.
//
// 테스트
// - http://localhost:8080/java-web/ex04/test07.html 실행
//
// 파라미터 이름만 넘어갈 때 getParameter()의 리턴 값은 빈 문자열 객체이다.
// null 이 아니다.
// 입력 상자에 값을 입력하지 않아도 빈 문자열이 서버에 전송된다는 것이다.
// => 예) a=aaa&b=
//
res.setContentType("text/plain;charset=UTF-8");
PrintWriter out = res.getWriter();
out.printf("a = %s\n", req.getParameter("a"));
out.printf("b = %s\n", req.getParameter("b"));
// 파라미터 이름 자체가 없으면 getParameter()는 null을 리턴한다.
out.printf("c = %s\n", req.getParameter("c"));
}
}
=>
ㄴ [전송] 선택
=>
ㄴ 파라미터 이름만 넘어갈 때 getParameter()의 리턴 값은 빈 문자열 객체임 (null 이 아님)
=> 입력 상자에 값을 입력하지 않아도 빈 문자열이 서버에 전송됨을 알 수 있음
ㄴ b= 다음에 빈 문자열이 있음
썸네일 이미지 만들기
test08.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test08</title>
</head>
<body>
<h1>파일 업로드 - 썸네일 이미지 만들기</h1>
<p>
업로드 파일을 가지고 특정 크기의 썸네일 이미지 파일을 따로 생성한다.
</p>
<form action="s8"
method="post"
enctype="multipart/form-data">
이름: <input type="text" name="name"><br>
나이: <input type="number" name="age"><br>
사진: <input type="file" name="photo"><br>
<input type="submit" value="전송">
</form>
</body>
</html>
Servlet08.java
// 썸네일 이미지 만들기
package eomcs.servlet.ex04;
import net.coobird.thumbnailator.ThumbnailParameter;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import net.coobird.thumbnailator.name.Rename;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebServlet("/ex04/s8")
public class Servlet08 extends GenericServlet {
private static final long serialVersionUID = 1L;
private String uploadDir;
@Override
public void init() throws ServletException {
this.uploadDir = this.getServletContext().getRealPath("/upload");
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
// 테스트
// - http://localhost:8080/java-web/ex04/test08.html 실행
//
req.setCharacterEncoding("UTF-8");
HttpServletRequest httpReq = (HttpServletRequest) req;
res.setContentType("text/html;charset=UTF-8");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head><title>servlet04</title></head>");
out.println("<body><h1>파일 업로드 결과</h1>");
// 일반 폼 데이터를 원래 하던 방식대로 값을 꺼낸다.
out.printf("이름=%s<br>\n", httpReq.getParameter("name"));
out.printf("나이=%s<br>\n", httpReq.getParameter("age"));
// 파일 데이터는 getPart()를 이용한다.
Part photoPart = httpReq.getPart("photo");
if (photoPart.getSize() == 0) {
out.println("</body></html>");
return;
}
// 파일을 선택해서 업로드 했다면,
String filename = UUID.randomUUID().toString();
photoPart.write(this.uploadDir + "/" + filename);
// 원본 사진을 가지고 특정 크기의 썸네일 이미지를 만들기
// 1) 썸네일 이미지를 생성해주는 자바 라이브러리 추가
// => mvnrepository.com에서 thumbnailator 라이브러리 검색
// => build.gradle 에 추가
// => '$ gradle eclipse' 실행
// => eclise IDE에서 프로젝트 리프래시
// 2) 썸네일 이미지 만들기
// => 원본 이미지 파일이 저장된 경로를 알려주고
// 어떤 썸네일 이미지를 만들어야 하는지 설정한다.
Thumbnails.Builder<File> thumbnailBuilder = Thumbnails.of(this.uploadDir + "/" + filename);
thumbnailBuilder.size(20, 20);
thumbnailBuilder.outputFormat("jpg");
thumbnailBuilder.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
Thumbnails.Builder<File> thumbnailBuilder2 = Thumbnails.of(this.uploadDir + "/" + filename);
thumbnailBuilder2.size(20, 20);
thumbnailBuilder2.crop(Positions.CENTER);
thumbnailBuilder2.outputFormat("jpg");
thumbnailBuilder2.toFiles(new Rename() {
@Override
public String apply(String name, ThumbnailParameter param) {
return name + "_20x20";
}
});
Thumbnails.of(this.uploadDir + "/" + filename).size(80, 80).outputFormat("jpg")
.crop(Positions.CENTER)
// .toFiles(Rename.PREFIX_DOT_THUMBNAIL);
.toFiles(new Rename() {
@Override
public String apply(String name, ThumbnailParameter param) {
return name + "_80x80";
}
});
Thumbnails.of(this.uploadDir + "/" + filename).size(160, 160).outputFormat("jpg")
.crop(Positions.CENTER)
// .toFiles(Rename.PREFIX_DOT_THUMBNAIL);
.toFiles(new Rename() {
@Override
public String apply(String name, ThumbnailParameter param) {
return name + "_160x160";
}
});
out.printf("사진=%s<br>\n", filename);
out.printf("<img src='../upload/thumbnail.%s.jpg'><br>\n", filename);
out.printf("<img src='../upload/%s_20x20.jpg'><br>\n", filename);
out.printf("<img src='../upload/%s_80x80.jpg'><br>\n", filename);
out.printf("<img src='../upload/%s' height='80'><br>\n", filename);
out.printf("<img src='../upload/%s_160x160.jpg'><br>\n", filename);
out.printf("<img src='../upload/%s'><br>\n", filename);
out.println("</body></html>");
}
}
=>
ㄴ upload 폴더에 있는 이미지 파일 모두 삭제해주기
=>
ㄴ [전송] 선택
=>
네이버클라우드 서비스 알아보기
Cloud Functions 알아보기
ㄴ Compute > Cloud Functions
=>
=>
'네이버클라우드 > JAVA 웹 프로그래밍' 카테고리의 다른 글
JAVA 63일차 (2023-08-22) 자바 프로그래밍_Servlet 정리 ex06~ex08 (0) | 2023.08.22 |
---|---|
JAVA 62일차 (2023-08-21) 자바 프로그래밍_Servlet 정리_ex05 (0) | 2023.08.21 |
JAVA 62일차 (2023-08-21) 자바 프로그래밍_Servlet 정리_ex01~ex02 (0) | 2023.08.21 |
JAVA 61일차 (2023-08-18) 자바 프로그래밍_64. JSP를 이용하여 콘텐트 출력문을 자동 생성하기(소개)_개인프로젝트 - 마트 관리 시스템 (0) | 2023.08.21 |
JAVA 61일차 (2023-08-18) 자바 프로그래밍_인텔리제이 설치 및 설정_Mac M1 기준 (0) | 2023.08.19 |