본문 바로가기
수업내용

20230607 수업내용🫡🫡🫡

by titlejjk 2023. 6. 7.

정규 표현식 객체 만드는 방법

1. let reg = /표현식/;

2. let reg = new RegExp("표현식");

 

let isMatch = reg.test(검증할 문자열)

bootstrap에 있는 기능중 form-floating을 활용해 효과를 넣어 주어보았다. 이 form-floating을 사용할 때에는 placeholder를 꼭 써주어야한다.

지난 시간에 board_file이라는 테이블을 만들어 자료실 목록을 구현하는 테이블을 만들어 보았다.

이번 시간에는 다운로드를 하는 페이지르 만들어 볼 것이다.

먼저 GET방식 파라미터로 전달되는 다운로드 시켜줄 파일의 번호를 읽어온다.

int num = Integer.parseInt(request.getParameter("num"));

다운로드 시켜주기 위해서는 3가지 정보가 필요하다.

어떤 파일명으로 upload 폴더에 저장되어 있는지?

업로드 당시 원본 파일명은 무엇인지?

파일의 사이즈는 어떻게 되는지?

이를 위해서 파일의 번호를 읽어오는 함수를 dao에 만들어준다.

package test.file.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import test.file.dto.FileDto;
import test.util.DbcpBean;

public class FileDao {
	
	private static FileDao dao;
	
	private FileDao() {}
	
	public static FileDao getInstance() {
		if(dao == null) {
			dao = new FileDao();
		}return dao;
	}
	//파일 하나의 정보를 리턴해주는 메소드
	public FileDto getData(int num) {
		FileDto dto = null;
		//필요한 객체의 참조값을 담을 지역변스 미리 만들기
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			//DbcpBean 객체를 이용해서 Connection 객체를 얻어온다(Connection pool 에서 얻어오기)
			conn = new DbcpBean().getConn();
			//실행할 sql 문 
			String sql = "SELECT writer, title, orgFileName, saveFileName, fileSize, regdate"
					+ " FROM board_file"
					+ " WHERE num = ?";
			pstmt = conn.prepareStatement(sql);
			//sql 문이 미완성이라면 여기서 완성
			pstmt.setInt(1, num);
			//select 문 수행하고 결과 값 받아오기
			rs = pstmt.executeQuery();
			//반복문 돌면서 Resultset 에 담긴 내용추출
			while (rs.next()) {
				dto = new FileDto();
				dto.setNum(num);
				dto.setWriter(rs.getString("writer"));
				dto.setTitle(rs.getString("title"));
				dto.setOrgFileName(rs.getString("orgFileName"));
				dto.setSaveFileName(rs.getString("saveFileName"));
				dto.setFileSize(rs.getLong("fileSize"));
				dto.setRegdate(rs.getString("regdate"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close();//Connection 이 Connection pool 에 반납된다
			} catch (Exception e) {
			}
		}
	}
	public List<FileDto> getList(){
		List<FileDto> list = new ArrayList<>();
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			conn = new DbcpBean().getConn();
			String sql = "SELECT num, writer, title, orgfilename, fileSize, regdate"
					+ " FROM board_file"
					+ " ORDER BY num DESC";
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			while(rs.next()) {
				FileDto dto = new FileDto();
				dto.setNum(rs.getInt("num"));
				dto.setWriter(rs.getString("writer"));
				dto.setTitle(rs.getString("title"));
				dto.setOrgFileName(rs.getString("orgFilename"));
				dto.setFileSize(rs.getLong("fileSize"));
				dto.setRegdate(rs.getString("regdate"));
				
				list.add(dto);
			}
		}catch(SQLException e) {
			e.printStackTrace();
		}finally {
			try {
				if(rs != null)
					rs.close();
				if(pstmt != null)
					pstmt.close();
				if(conn != null)
					conn.close();
			}catch(Exception e) {	
			}
		}return list;
	}	
	   
	   //업로드된 파일 정보를 DB 에 저장하는 메소드
	   public boolean insert(FileDto dto) {
	      Connection conn = null;
	      PreparedStatement pstmt = null;
	      int rowCount = 0;
	      try {
	         conn = new DbcpBean().getConn();
	         String sql = "INSERT INTO board_file"
	               + " (num, writer, title, orgFileName, saveFileName, fileSize, regdate)"
	               + " VALUES(board_file_seq.NEXTVAL, ?, ?, ?, ?, ?, SYSDATE)";
	         pstmt = conn.prepareStatement(sql);
	         // ? 에 바인딩할게 있으면 해주고
	         pstmt.setString(1, dto.getWriter());
	         pstmt.setString(2, dto.getTitle());
	         pstmt.setString(3, dto.getOrgFileName());
	         pstmt.setString(4, dto.getSaveFileName());
	         pstmt.setLong(5, dto.getFileSize());
	         // INSERT OR UPDATE OR DELETE 문을 수행하고 수정되거나, 삭제되거나, 추가된 ROW 의 갯수 리턴 받기
	         rowCount = pstmt.executeUpdate();
	      } catch (Exception e) {
	         e.printStackTrace();
	      } finally {
	         try {
	            if (pstmt != null)
	               pstmt.close();
	            if (conn != null)
	               conn.close();
	         } catch (Exception e) {
	         }
	      }
	      if (rowCount > 0) {
	         return true;
	      } else {
	         return false;
	      }
	   }
}

예를 들어서 

를 누르면 페이지 이동없이 다운로드가 진행이 된다. 파일 자체로 응답을 하는 것인데, 다운로드 했으면 파일을 만들어주어야하는데 이때 만들어지는게 크게 3가지 정보가 들어오게된다. 파일명, 파일의크기, 파일을 구성하고 있는 byte.

우린 여태껏 무언가를 요청하면 페이지 이동을 한다던가 했는데 다운로드 요청은 무언가 다르다.

파일데이터는 지금수업에서는 파일데이터는 따로 폴더에 따로 저장해두었는데 반복문을 통해 파일을 구성하고 있는 byte를 보내주어야 한다.

파일명을 누르면 다운로드가 동작하도록 download.jsp에서 작동하도록 해주어야 한다.

<%@page import="java.io.File"%>
<%@page import="java.io.BufferedOutputStream"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="java.io.FileInputStream"%>
<%@page import="test.file.dao.FileDao"%>
<%@page import="test.file.dto.FileDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//1. GET 방식 파라미터로 전달되는 다운로드 시켜줄 파일의 번호를 읽어온다.
	int num = Integer.parseInt(request.getParameter("num"));
	/*
	  2. DB 에서 다운로드 시켜줄 파일의 정보를 읽어온다.
	  	 어떤 파일명으로 upload 폴더에 저장되어 있는지 => saveFileName
	  	 업로드 당시 원본 파일명은 무엇인지 => orgFileName
	  	 파일의 사이즈는 어떻게 되는지 => fileSize
	  	 -다운로드 시켜주기 위해서는 위의 3가지 정보가 필요하다.
	*/
	   FileDto dto=FileDao.getInstance().getData(num);
    //3. 서버의 파일시스템(upload) 에 저장된 파일에서 바이트 알갱이를 읽어서 출력한다(다운로드)
    String orgFileName=dto.getOrgFileName();
    String saveFileName=dto.getSaveFileName();
    //다운로드 시켜줄 파일의 실제 경로 구성하기 
    String path=application.getRealPath("/upload") + File.separator+saveFileName;
    //다운로드할 파일에서 읽어들일 스트림 객체 생성하기
    FileInputStream fis=new FileInputStream(path);
    //다운로드 시켜주는 작업을 한다. (실제 파일 데이터와 원본파일명을 보내줘야한다.)
    
    //다운로드 시켜줄 파일명 인코딩  
    String encodedName=URLEncoder.encode(orgFileName, "utf-8");
 //파일명에 공백이있는 경우 처리 
 encodedName=encodedName.replaceAll("\\+"," ");
    
    
    //응답 헤더 정보 설정
    response.setHeader("Content-Disposition","attachment;filename="+encodedName);
    response.setHeader("Content-Transfer-Encoding", "binary");
    
    //다운로드할 파일의 크기 읽어와서 다운로드할 파일의 크기 설정
    response.setContentLengthLong(dto.getFileSize());
    
    //Exception 발생하지 않도록 (response.getOutputStream() 호출전에 해야한다)
    out.clear();
    out=pageContext.pushBody();
    
    //클라이언트에게 출력할수 있는 스트림 객체 얻어오기
    BufferedOutputStream bos=
       new BufferedOutputStream(response.getOutputStream());
    //한번에 최대 1M byte 씩 읽어올수 있는 버퍼
    byte[] buffer=new byte[1024*1024];
    int readedByte=0;
    //반복문 돌면서 출력해주기
    while(true){
       //byte[] 객체를 이용해서 파일에서 byte 알갱이 읽어오기
       readedByte = fis.read(buffer);
       if(readedByte == -1)break; //더이상 읽을 데이터가 없다면 반복문 빠져 나오기
       //읽은 만큼 출력하기
       bos.write(buffer, 0, readedByte);
       bos.flush(); //출력
    }
    //FileInputStream 닫아주기 
    fis.close();   
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/download.jsp</title>
</head>
<body>

</body>
</html>

파일의 절대 경로를 얻어내는 방법은 application.getRealPath("/upload")

파일 구분자를 얻어내는 법은 File.separator

파일면은 saveFileName;

이런식이다.

이걸 다 연결하면 전체 full path가 나온다. 이걸 통해 얻은 문자열을

new FileIputStream(); 에 넣어준 것이다.

FileputStream()을 통해 바이트 조각들을 읽어들여서 다운로드 하는 것이다.

 

이렇게 들어간 fis를 반복문을 돌면서 읽어들인다.

지금까지는 클라이언트 웹브라우저에게 문자열을 응답을 했지만 지금은 file데이터를 응답해야하기 때문에

HttpServletReponse에 있는 메소드를 사용하고 

BufferedOutputStream으로 포장을 해서 반복문으로 돌면서 출력을 해준다.

자바 입출력할때 많이 사용했다.

그리고 contentType은 application/octet-stream으로 바꿔주는것이좋다.

삭제 기능을 구현하는데 본인이 올린 것은 삭제가 가능하게 해야겠지만 다른사람이 올린 것을 삭제가 안되게 하는방법에대해서 알아보겠다.

우선 글의 작성자와 로그인된 아이디와 같을 때만 출력되도록 코드를 작성해야한다.

글의 작성자는 tmp.getWriter()

로그인된 아이디는 String id = (String)session.getAttribute("id");

이렇게 두개가 같을 때 출력이 되도록 코드해야한다.

String id = (String)session.getAttribute("id");
//session 은 HttpSession type을 통해 로그인된 id를 얻어올 수 있다.
<td>
	<%-- 글작성자와 로그인된 아이디와 같을 때만 삭제 링크 출력하기 --%>
	<%if(tmp.getWriter().equals(id)){ %>
		<a href = "delete.jsp?num=<%=tmp.getNum() %>">삭제</a>
	<%} %>
</td>

이렇게 하면 로그인된 상태에서 동일한 작성자일 때만 삭제 버튼이 출력된다.

그럼 delete.jsp페이지에서는 무엇을해야할까?

1.삭제할 파일 번호를 이용해서 파일정보를 얻어온다.

2.해당 파일을 파일시스템에서 실제로 삭제한다.

3. db에서 해당 파일의 정보를 삭제한다.

4.응답한다.

delete.jsp를 만들어 준 후에

삭제할 파일의 번호를 가져오는 코드를 먼저 적어준다.

int num = Integer.parseInt(request.getParameter("num"));

삭제할파일의 정보를 DB에서 읽어오고

FileDto dto = FileDao.getInstance().getData(num);

삭제할 파일을 객체로 만들어주고 그 객체안에는 삭제할 경로를 지정해준다.

String realPath = application.getRealPath("/upload") + File.separator+dto.getSaveFileName();
	File fis = new File(realPath);

삭제할 메소드를 적어준다.

fis.delete();

db에서 삭제할 메소드를 만들어준다.

//삭제하는 메소드
	public boolean delete(int num) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		int rowCount = 0;
		try {
			//DbcpBean 객체를 이용해서 Connection 객체를 얻어온다(Connection Pool 에서 얻어오기)
			conn = new DbcpBean().getConn();
			//실행할 sql 문 
			String sql = "DELETE FROM board_file"
					+ " WHERE num = ? ";
			pstmt = conn.prepareStatement(sql);
			//sql 문이 미완성이라면 여기서 완성
			pstmt.setInt(1, num);
			//select 문 수행하고 결과값 받아오기
			rowCount = pstmt.executeUpdate();
			//반복문 돌면서 ResultSet 에 담긴 내용 추출
		} catch (SQLException se) {
			se.printStackTrace();
		} finally {
			try {
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close();
			} catch (Exception e) {
			}
		}
		//만일 변화된 row 의 갯수가 0 보다 크면 작업 성공
		if (rowCount > 0) {
			return true;
		} else {
			return false;
		}

	}

DB에서 삭제하기 delete.jsp로 와서

FileDao.getInstance().delete(num);
String cPath = request.getContextPath();
	response.sendRedirect(cPath + "/file/list.jsp");//파일목록보기로 이동

로그인된 아이디와 글의 작성자가 일치하는지 확인하기

response객체에는 sendError라는 메소드가 있다. 인자로는 int sc, String msg를 넣어준다.

int sc 에는 예를들어 404/500/400등이 있다.

String id = (String)session.getAttribute("id");
	if(dto.getWriter().equals(id)){
		//에러 응답하기
		response.sendError(403, "어허!");
		//메소드를 여기서 끝내기
		return;
	}

로그인할때 내 아이디에 링크를 걸기

<a href="${pageContext.request.contextPath}/users/private/info.jsp"><%=id %></a><strong><%=id %></strong> 님 어서오고!

왜 private에다 했냐면 login을 해야만 보이게끔하기 위해서이다.

/users/private/info.jsp를 만들어 준후에

1. 로그인상태에서 들어가기 때문에 

String id = (String)session.getAttribute("id");

로 작성해준 후에

2. DB에서 가입정보를 불러오기 위해

UserDao에서 조회하는 함수를 id를넘겨주면 해당 id에대해서 select하는 메소드를 만들어준다.

public UsersDto getData(String id) {
		UsersDto dto = null;
		//필요한 객체의 참조값을 담을 지역변스 미리 만들기
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			//DbcpBean 객체를 이용해서 Connection 객체를 얻어온다(Connection pool 에서 얻어오기)
			conn = new DbcpBean().getConn();
			//실행할 sql 문 
			String sql = "SELECT pwd, email, profile, regdate"
					+ " FROM users"
					+ " WHERE id = ?";
			pstmt = conn.prepareStatement(sql);
			//sql 문이 미완성이라면 여기서 완성
			pstmt.setString(1, id);
			//select 문 수행하고 결과 값 받아오기
			rs = pstmt.executeQuery();
			//반복문 돌면서 Resultset 에 담긴 내용추출
			while (rs.next()) {
				dto = new UsersDto();
				dto.setId(id);
				dto.setPwd(rs.getString("pwd"));
				dto.setEmail(rs.getString("email"));
				dto.setProfile(rs.getString("profile"));
				dto.setRegdate(rs.getString("regdate"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close();//Connection 이 Connection pool 에 반납된다
			} catch (Exception e) {
			}
		}return dto;
	}
<%@page import="test.users.dto.UsersDto"%>
<%@page import="test.users.dao.UsersDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//1. session scope에서 로그인된 아이디 불러오기
	String id = (String)session.getAttribute("id");
	//2. DB에서 가입 정보를 불러온다.
	UsersDto dto = UsersDao.getInstance().getData(id);
	//3.응답한다.
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/private/info.jsp</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" />
</head>
<body>
	<div class="contaner">
		<h1>가입 정보 입니다.</h1>
		<table>
			<tr>
				<th>아이디</th>
				<td><%=dto.getId() %></td>
			</tr>
			<tr>
				<th>프로필 이미지</th>
				<td>
					<%if(dto.getProfile() == null){ %>
						등록된 이미지 없음
					<%}else{ %>
						<img src = "${pageContext.request.contextPath}<%=dto.getProfile()%>" />
					<%} %>
				</td>
			</tr>
			<tr>
				<th>비밀번호</th>
				<td>
					<a href="pwd_updateform.jsp">수정하기</a>
				</td>
			</tr>
			<tr>
				<th>이메일</th>
				<td><%=dto.getEmail() %></td>
			</tr>
			<tr>
				<th>가입일</th>
				<td><%=dto.getRegdate() %></td>
			</tr>
		</table>
	</div>
</body>
</html>

이미지를 등록하게 해주자

먼저 private폴더 안에 profile_uploadform.jsp를만들어 준 후에

<form action="upload.jsp" method = "post" enctype = "multipart/form-data">
		<label for = "image">이미지</label>
		<input type = "file" name = "image" id = "image" accept = ".jpg, .jpeg, .png, .gif, .JPG, .JPEG"/>
		<button type = "submit">업로드</button>
	</form>

이런 양식으로 form을 만들어주었다.

accept는 적어둔 확장자만 올릴수 있다.

그럼 이제 upload를 할수 있게 upload.jsp를 만들어 보자. 만든 후에

//로그인된 아이디
	String id = (String)session.getAttribute("id");
//1. 파일을 저장할 서버에서의 실제 경로 구성
	String realPath = application.getRealPath("/upload");
	//2. 아래의 MultipartRequest 객체가 예외 없이 잘 생성되면 webapp/upload 폴더에 파일이 자동으로 저장된다.
	MultipartRequest mr = new MultipartRequest(request,
			realPath,
			1024*1024*50,
			"utf-8",
			new DefaultFileRenamePolicy()
			);
	//3. MultipartRequest 객체의 메소드를 이용해서 폼전송된 내용을 추출해야한다.
	String saveFileName = mr.getFilesystemName("image");//저장된 파일명
	//DB에 저장할 이미지 경로 구성하기
	String imagePath = "/upload/" + saveFileName;

를 작성해준다.

그런다음 UsersDao에 id와 profile을 가져와 프로필 이미지 경로를 수정하는 메소드를 만든다.

//프로필 이미지 경로를 수정하는 메소드
	public boolean updateProfile(UsersDto dto) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		int rowCount = 0;
		try {
			//DbcpBean 객체를 이용해서 Connection 객체를 얻어온다(Connection Pool 에서 얻어오기)
			conn = new DbcpBean().getConn();
			//실행할 sql 문 
			String sql = "UPDATE users"
					+ " SET profile =?"
					+ " WHERE id = ?";
			pstmt = conn.prepareStatement(sql);
			//sql 문이 미완성이라면 여기서 완성
			pstmt.setString(1, dto.getProfile());
			pstmt.setString(2, dto.getId());
			//select 문 수행하고 결과값 받아오기
			rowCount = pstmt.executeUpdate();
			//반복문 돌면서 ResultSet 에 담긴 내용 추출
		} catch (SQLException se) {
			se.printStackTrace();
		} finally {
			try {
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close();
			} catch (Exception e) {
			}
		}
		//만일 변화된 row 의 갯수가 0 보다 크면 작업 성공
		if (rowCount > 0) {
			return true;
		} else {
			return false;
		}

	}

그다음 upload.jsp를 완성해준다

<%@page import="test.users.dao.UsersDao"%>
<%@page import="test.users.dto.UsersDto"%>
<%@page import="com.oreilly.servlet.MultipartRequest"%>
<%@page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//로그인된 아이디
	String id = (String)session.getAttribute("id");
	
	//1. 파일을 저장할 서버에서의 실제 경로 구성
	String realPath = application.getRealPath("/upload");
	//2. 아래의 MultipartRequest 객체가 예외 없이 잘 생성되면 webapp/upload 폴더에 파일이 자동으로 저장된다.
	MultipartRequest mr = new MultipartRequest(request,
			realPath,
			1024*1024*50,
			"utf-8",
			new DefaultFileRenamePolicy()
			);
	//3. MultipartRequest 객체의 메소드를 이용해서 폼전송된 내용을 추출해야한다.
	String saveFileName = mr.getFilesystemName("image");//저장된 파일명
	//DB에 저장할 이미지 경로 구성하기
	String imagePath = "/upload/" + saveFileName;
	//DB에 수정반영하고
	UsersDto dto = new UsersDto();
	dto.setId(id);
	dto.setProfile(imagePath);
	UsersDao.getInstance().updateProfile(dto);
	//리다이렉트응답하기
	String cPath = request.getContextPath();
	response.sendRedirect(cPath + "/users/private/info.jsp");
%>

그런데 이미지가 너무 크게 나온다.

이는 info.jsp에서 css로 수정해주면된다.

<%@page import="test.users.dao.UsersDao"%>
<%@page import="test.users.dto.UsersDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
   //1. session scope 에서 로그인된 아이디 불러오기
   String id=(String)session.getAttribute("id");
   //2. DB 에서 가입 정보를 불러온다.
   UsersDto dto=UsersDao.getInstance().getData(id);
   //3. 응답한다.
%>      
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/users/private/info.jsp</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" />
<style>
   #profileImage{
      width:100px;
      height: 100px;
      border:1px solid #cecece;
      border-radius:50%
   }
</style>
</head>
<body>
   <div class="container">
      <h1>가입 정보 입니다.</h1>
      <table>
         <tr>
            <th>아이디</th>
            <td><%=dto.getId() %></td>
         </tr>
         <tr>
            <th>프로필 이미지</th>
            <td>
               <%if(dto.getProfile() == null){ %>
                  등록된 이미지 없음
                  <a href="profile_uploadform.jsp">등록하기</a>
               <%}else{ %>
                  <img id="profileImage" src="${pageContext.request.contextPath }<%=dto.getProfile() %>" />
               <%} %>
            </td>
         </tr>
         <tr>
            <th>비밀번호</th>
            <td>
               <a href="pwd_updateform.jsp">수정하기</a>
            </td>
         </tr>
         <tr>
            <th>이메일</th>
            <td><%=dto.getEmail() %></td>
         </tr>
         <tr>
            <th>가입일</th>
            <td><%=dto.getRegdate() %></td>
         </tr>
      </table>
   </div>
</body>
</html>
border-radius : 50%;

을 사용하면 곡률반경이 50%이기 때문에 동그랗게 나온다. 모서리의 딱 맞는 원을 그렸을때 이미지의 폭의 50%만 사용한다는 의미이다.

 너무 길어서 하나더...

댓글