본문 바로가기

programming/Gukbi

국비 교육 80일차 - spring file upload, spring5개발

728x90
반응형

이제까지 실습을 spring4버전으로 했는데, spring5버전에 맞는 자바로만 하는 개발방법을 배웠다. 

나머지는 다 xml을 이용한 방식과 똑같은데 한가지 다른 점은 config폴더에 들어가는 xml 파일들에 해당하는 내용이 전부 자바로 이루어져 있다는 점이다. 

 

 

먼저 jsp화면을 찾아 띄워주는 viewResolver 는 아래와 같이 자바로 구현할 수 있다. 

@Bean
	public ViewResolver viewResolver()
	{
		InternalResourceViewResolver r=new InternalResourceViewResolver();
		r.setPrefix("/");
		r.setSuffix(".jsp");
		return r;
	}

 InternalResourceViewResolver라는 객체를 생성해서 셋팅이 가능하다. 

 

 

그리고 클래스에서 WebMvcConfigurer를 상속받으면 dispatcherServlet에 연결할 수 있는 메소드를 오버라이딩 할 수 있다. 

@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		    configurer.enable();
	}

 

datasource에 해당하는 메소드

@Bean
	public DataSource dataSource()
	{
		BasicDataSource ds=new BasicDataSource();
		ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
		ds.setUrl("jdbc:oracle:thin:@localhost:1521:XE");
		ds.setUsername("hr");
		ds.setPassword("happy");
		ds.setMaxActive(10);
		ds.setMaxIdle(10);
		ds.setMaxWait(-1);
		return ds;
	}

 

 

sql문장을 찾아주는 sqlsessionfactory 메소드

@Bean
	public SqlSessionFactory sqlSessionFactory() throws Exception
	{
		SqlSessionFactoryBean ssf=new SqlSessionFactoryBean();
		ssf.setDataSource(dataSource());
		Resource res=new ClassPathResource("Config.xml");
		ssf.setConfigLocation(res);
		return ssf.getObject();
	}

 그리고 이후에 동작하는 것은 전과 같다. 

 

 

 

그 다음에 연습해본건 자료를 업로드 하는 객체를 활용하는 방법

자료 여러개를 올리기 위해서 필요한 클래스를 application-context에 먼저 등록해준다. 

 

 <bean id="multipartResolver"
       class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
    />

 라이브러리에서 CommomsMultipartResolver라는 객체를 불러온다. 컨테이너에 등록해두면 여러개의 파일을 업로드하는 기능을 사용할 수 있다. 

 

그리고 mapper에서 기능을 처리해줄 sql문장을 써준다. 

<!-- 등록  -->
  <insert id="databoardInsert" parameterType="com.sist.dao.DataBoardVO">
    <!-- Sequence처리 -->
    <selectKey keyProperty="no" resultType="int" order="BEFORE">
      SELECT NVL(MAX(no)+1,1) as no FROM spring_databoard
    </selectKey>
    INSERT INTO spring_databoard VALUES(
      #{no},
      #{name},
      #{subject},
      #{content},
      #{pwd},
      SYSDATE,
      0,
      #{filename},
      #{filesize},
      #{filecount}
    )
  </insert>

 다른 부분은 일반 게시판과 같기 때문에 생략

 

글을 등록해줄때 어떻게 처리해주고 있는지 보겠다. 먼저 글을 등록할때 글 번호를 자동적으로 1씩 증가시켜주기 위해 

시퀀스 처리를 아래와 같이 해준다. 

<!-- Sequence처리 -->
    <selectKey keyProperty="no" resultType="int" order="BEFORE">
      SELECT NVL(MAX(no)+1,1) as no FROM spring_databoard
    </selectKey>

 no 값을 1 증가시켜 주는것이기 때문에 keyProperty는 no, resultType은 당연히 int, 명령은 insert를 실행하기 전에 먼저 실행하라고 BEFORE을 써준다. 

 

그 이후에는 insert문을 작성해준다. 

 

INSERT INTO spring_databoard VALUES(
      #{no},
      #{name},
      #{subject},
      #{content},
      #{pwd},
      SYSDATE,
      0,
      #{filename},
      #{filesize},
      #{filecount}
    )
  </insert>

 파일이름, 사이즈, 파일이 몇개인지 세주는 count변수를 저장해준다. 

 

상세보기에서는 역시 조회수를 1 증가시켜준 다음 글 내용을 가져와 주기 위해서 sql문을 써준다. 

 <!-- 상세보기 -->
  <update id="databoardHitIncrement" parameterType="int">
    UPDATE spring_databoard SET
    hit=hit+1
    WHERE no=#{no}
  </update>
  <select id="databoardDetailData" resultType="com.sist.dao.DataBoardVO" parameterType="int">
    SELECT * FROM spring_databoard 
    WHERE no=#{no}
  </select>

 

그리고 삭제를 위한 sql문장까지 작성해주면 완성

 <!-- 비밀번호 확인 -->
  <select id="databoardGetPassword" resultType="string" parameterType="int">
   SELECT pwd FROM spring_databoard
   WHERE no=#{no}
  </select>
  <!-- 파일명 읽기 -->
  <select id="databoardFileInfoData" resultType="com.sist.dao.DataBoardVO" parameterType="int">
  SELECT filename, filecount FROM spring_databoard
  WHERE no=#{no}
  </select>
  <!-- 삭제 -->
  <delete id="databoardDelete" parameterType="int">
  DELETE FROM spring_databoard
  WHERE no=#{no}
  </delete>

 

확실히 myBatis를 사용하니 sql문장을 쓰는것은 한결 간결해졌다. 

 

DAO에서 처리해주는 부분

// 상세보기
	// databoardHitIncrement
	public DataBoardVO databoardDetailData(int no)
	{
		// 조회수 증가 
		getSqlSession().update("databoardHitIncrement",no);
		// 실제데이터 읽기
		return getSqlSession().selectOne("databoardDetailData", no);
	}

 상세보기할때 처리해주는 부분. 조회수를 증가시키고 글번호를 주고 실제 데이터를 insert해준다. 

 

// 삭제 
	public DataBoardVO databoardFileInfoData(int no)
	{
		return getSqlSession().selectOne("databoardFileInfoData", no);
	}
	public boolean databoardDelete(int no, String pwd)
	{
		boolean bCheck=false;
		String db_pwd=getSqlSession().selectOne("databoardGetPassword", no);
		if(db_pwd.equals(pwd))
		{
			bCheck=true;
			getSqlSession().delete("databoardDelete", no);
		}
		return bCheck;
	}

 글을 삭제할때는 저장되어 있는 파일도 같이 삭제되어야지 서버 컴퓨터의 용량을 아낄 수 있기 때문에 삭제하기전에 삭제할 글에서 파일 정보를 받아오는 메소드를 작성해준다. 

 

그리고 삭제할때는 사용자가 입력한 비밀번호와 테이블에 저장되어 있는 비밀번호를 비교해서 맞으면 삭제 메소드를 실행할 수 있도록 하는 메소드를 만든다. 

 

그리고 필요한 작업을 dispatcher에 요청하기 위해서 controller파일을 만든다. 

 

그 전에 먼저, 

 

 글을 써서 파일을 테이블에 저장하는 과정이다. 파일은 여러개를 처리해주기 위해 파일명을 배열에 담아뒀다. 

<script type="text/javascript">
let fileIndex=0;
$(function(){
	$('#addBtn').click(function(){
		$('#user-table').append(
		   '<tr id="m'+fileIndex+'">'
		  +'<td>파일 '+(fileIndex+1)+':<input type=file name=files['+fileIndex+'] size=20>'
		  +'</td></tr>'
		)
		fileIndex++;
	});
	$('#delBtn').click(function(){
		if(fileIndex!=0)
		{
		   $('#m'+(fileIndex-1)).remove();	
		   fileIndex--;
		}
	})
});
</script>

 파일 인덱스를 0값으로 초기화 해두고, files[] 배열에 인덱스를 0부터 저장해준다.

 

VO에서도 파일들을 처리할 수 있는 List배열을 만들어뒀다. 

private List<MultipartFile> files; // 파일 업로드 했을때 저장되는 공간 => 여러개가 넘어온다
	/*
	 * 	<input type=file name="files[0]">
	 * 	<input type=file name="files[1]">
	 * 	<input type=file name="files[2]">
	 */
else // 업로드가 된 상태 
	   {
		   String tempFile="";
		   String tempSize="";		   
		   for(MultipartFile mf:list)
		   {
			   try
			   {
				  String strFile=mf.getOriginalFilename(); 
				  File file=new File("c:\\spring-upload\\"+strFile);
				  mf.transferTo(file);// 서버에 업로드(서버 폴더에 파일 저장)
				  // 오라클에 파일명을 묶어서 저장  a.jpg,b.png,c.jpg
			      tempFile+=strFile+",";
			      tempSize+=file.length()+",";
			   }catch(Exception ex){}
			   
		   }
		   tempFile=tempFile.substring(0,tempFile.lastIndexOf(","));
		   tempSize=tempSize.substring(0,tempSize.lastIndexOf(","));
		   vo.setFilename(tempFile);
		   vo.setFilesize(tempSize);
		   vo.setFilecount(list.size());
	   }
	   dao.databoardInsert(vo);
	   return "redirect:list.do";

 

 파일이 업로드가 되었을때 위와 같이 처리를 해준다. ,를 전부 붙여서 저장해줬기 때문에 오라클 테이블에 넣기 전에는 마지막 ,를 제거하고 넣어준다. 그리고 다시 list.do로 넘어갈 수 있게 처리를 한다. 

 

 

다음은 상세보기 

// 상세보기 : 출력 화면 => String
   @GetMapping("databoard/detail.do")
   public String databoard_detail(int no,Model model)
   {
	   DataBoardVO vo=dao.databoardDetailData(no);
	   List<String> fn=new ArrayList<String>();
	   List<String> fs=new ArrayList<String>();
	   String s1=vo.getFilename();
	   String s2=vo.getFilesize();
	  
	  if(vo.getFilecount()!=0)
	  {
		   StringTokenizer st=new StringTokenizer(s1,",");
		   while(st.hasMoreTokens())
		   {
			   fn.add(st.nextToken());
		   }
		   
		   st=new StringTokenizer(s2,",");
		   while(st.hasMoreTokens())
		   {
			   fs.add(st.nextToken());
		   }
		   model.addAttribute("fn", fn);
		   model.addAttribute("fs", fs);
	  }
	   model.addAttribute("vo", vo);
	  
	   return "databoard/detail";
   }

 오라클에서는 여러개의 파일명과 파일사이즈를 한 문자열로 저장했기 때문에, 자바에서는 다시 List에서 나눠서 저장을 해준다. 그래서 StringTokenizer를 사용해줬다. 

 

<c:if test="${vo.filecount!=0 }">
         <tr>
           <th width=20% class="danger text-center">첨부파일</th>
           <td colspan="3">
             <ul style="list-style-type: none">
               <c:forEach var="f" items="${fn }" varStatus="s">
                 <li><a href="download.do?fn=${f }">${f }</a>(${fs[s.index]}Bytes)</li>
               </c:forEach>
             </ul>
           </td>
         </tr>
       </c:if>

 

그러면 위와 같이 첨부파일 여러개를 상세보기에서 띄울 수 있게 된다.  

 

서버 컴퓨터에 이렇게 저장이 됐다. 

 

그리고 이걸 다운로드 받을 수 있게 만들어주는 controller

 // 다운로드 : void
   @GetMapping("databoard/download.do")
   public void databoard_download(String fn,HttpServletResponse response)
   {
	   try
	   {
		   File file=new File("c:\\spring-upload\\"+fn);
		   // 파일 정보 얻기 
		   response.setHeader("Content-Disposition", "attachement;filename="
				       +URLEncoder.encode(fn, "UTF-8"));
		   response.setContentLength((int)file.length());
		   
		   BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
		   // 서버에서 파일을 읽어 온다
		   BufferedOutputStream bos=new BufferedOutputStream(response.getOutputStream());
		   // 다운로드하는 사람에게 파일을 보내준다 
		   byte[] buffer=new byte[1024];
		   int i=0;// 읽은 바이트 
		   while((i=bis.read(buffer, 0, 1024))!=-1) //-1 file end => EOF
		   {
			   // 다운로드하는 사람에게 보내라 
			   bos.write(buffer, 0, i);
		   }
		   bis.close();
		   bos.close();
	   }catch(Exception ex){}
   }

 

 

다운로드가 이렇게 완료된다. 

 

 

삭제 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style type="text/css">
.row{
  width:800px;
  margin: 0px auto;
}
h1 {
  text-align: center;
}
</style>
</head>
<body>
  <div style="height: 50px"></div>
  <div class="container">
  <div class="row">
  <form method=post action="delete_ok.do">
  	<table vlass="table">
  		<tr>
  		 <td>비밀번호:<input type=password name=pwd size=10 class="input-sm"></td>
  		 <input type=hidden name=no value="${no }">
  		</tr>
  		<tr>
  			<td class="text-center">
  			<input type=submit value="삭제" class="btn btn-sm btn-danger">
  			<input type=button value="취소" class="btn btn-sm btn-danger"
  			 onclick="javascript:history.back()">
  			</td>
  		</tr>
  	</table>
  	</form>
  </div>
  </div>
 </
</body>
</html>

 jsp는 이렇게 되어 있다. 

 

delete_ok.do는 따로 컨트롤러를 빼서 만들었는데, ajax를 사용해서 사용할때는 model을 이용하지 않고, 바로 delete.jsp에 스크립트를 보내서 처리를 해주기도 한다 

@RequestMapping(value="databoard/delete_ok.do")
	public String databoard_delete_ok(int no, String pwd)
	{
		String msg="";
		// 데이터 베이스에 저장되어 있는 파일 받기
		DataBoardVO vo=dao.databoardFileInfoData(no);
		boolean bCheck=dao.databoardDelete(no, pwd);
		if(bCheck==true)
		{
			// 파일 지우기 => list.do이동
			msg="<script>location.href=\"list.do\";</script>";
			try
			{
				if(vo.getFilecount()!=0) // 파일이 있는 경우에만 삭제
				{
					StringTokenizer st=new StringTokenizer(vo.getFilename(), ",");
					while(st.hasMoreTokens())
					{
						File file=new File("c:\\spring-upload\\"+st.nextToken());
						file.delete();
					}
				}
			}catch(Exception ex){}
		}
		else
		{
			// 스크립트 => 비밀번호가 틀립니다 메세지
			msg="<script>"
					+"alert(\"Password Fail!!\");"
					+"history.back();"
					+"</script>";
		}
		return msg;
	}

 

비밀번호가 맞을 경우 파일이 있으면 지워주고, 아니면 비밀번호가 틀렸다는 메세지를 주고 뒤로 갈 수 있게 한다. 

 

그리고 msg를 리턴해서 바로 delete.jsp에 띄워준다. 

728x90
반응형