ABOUT ME

  • 이미지업로드: inputStream,outputStream, cos.jar-MultipartRequest
    JAVA 2021. 7. 25. 14:43

    이미지 업로드

    form 에 enctype="multipart/form-data" 속성과 값을 지정해야 한다. ( 이번에 쓸 API 와 관련된 환경타입 )
    파일을 request.getParameter 하면 파일의 이름, 정보등이 텍스트형식으로 얻어진다.
    파일 이름만 있으면 그 파일을 다운받거나 요청할 수 있기 때문이다.
    파일은 1. 파일저장 2. DB저장 의 두가지 방법이 있다.
    파일 업로드를 위한 API 는 cos.jar 라고 검색해서 mavenpository 홈페이지에서 다운 또는 maven dependency 태그로 넣자
    https://mvnrepository.com/artifact/servlets.com/cos/05Nov2002

    Servlet 책에서 library 에 tomcat안의 servlet API 를 경로 지정했던 이유는 eclipse 내에서 tomcat이 실행전이라 이 API 가 보이지 않자 미리부터 없다고 오류가 자꾸난다. ( 사실 실행될때 API 가 실행되기 때문에 문제는 없는데도 불구하고..)

    그래서 수업시간에는 책처럼 경로를 지정하지 않고 그냥 maven 의 pom.xml 에 dependency 로 library를 다운받았다.

    상대 form 에서 넘어온 파일을 저장할 폴더를 만든다. (upload 라는 이름으로 만들었다.)
    그 폴더의 실제 경로를 찾아낸다(절대 경로로는 운영체제에 따라 C:// 가 없을 수도 있기 때문에 C://upload 라 하면 문제가 될 수 있다.)
    request.getRealPath("폴더명")
    tomcat 에서는 tomcat 의 webapps 폴더안에 프로젝트를 복사해서 놓는데 upload폴더에 저장하는 로직을 구현해도
    내가 만든 프로젝트 경로에 들어가면 없다.
    왜냐하면 이는 tomcat이 가상 복제본으로 취급하여 사용하는 곳이고, 실제 경로에 저장했으니 실제 경로로 들어가보면 잘 되어 있다.
    실제 경로란 eclipse 가 내가 만든 위치가 아닌 자기가 따로 정보폴더에 저장해놓은 곳이다.

    >> input.html

    <body>
    
    <form action="receive.jsp" method="post" enctype="multipart/form-data">
    <h1>회원등록</h1>
    <!--기초내용 -->
    성명 <input type = "text" name = "name" id="name"><br>
    프로필<img src="images/sample.jpg" id="myimg"/><br>
    	<input type="file"	name="profile" id="myfile"/><br>
    아이디 <input type="text" name="id" id="id" readonly="readonly"> <input type="button" onclick="아이디중복검사창을띄우다()" value="조회"><br>
    패스워드 <input type="password" name="password" id="password1"/><br>
    <input type="submit" value="등록" />
    </form>
    
    </body>
    
    <script>
    	function 그림파일읽어출력하기(event){
    		var 그림파일 = event.target.files[0];
    		
    		if(!그림파일.type.match('image.*')){
    			alert("그림파일을 선택해주세요.");
    			return;
    		}
    		
    		var 파일리더 = new FileReader();
    		파일리더.onload = function(event2){
    			var myimg = document.querySelector("#myimg");
    			myimg.src = event2.currentTarget.result;
    		}
    		파일리더.readAsDataURL(그림파일);
    	}
    	
    	document.querySelector("#myfile").addEventListener("change",그림파일읽어출력하기,false);
    </script>
    업로드한 파일 저장하기
    다운받은 파일업로드관련 API 를 이용해보자 (cos.jar)
    1. 해당 API 의 클래스인 MultipartRequest 를 사용해야 한다. (form 의 환경타입이 multipart/form-data 인것 처럼 이름이 비슷)
    2. MultipartRequest 를 생성할 때 생성자의 매개값에
    1) 파일을 받아오는 request
    2) 파일저장경로
    3) 파일최대용량
    4) 인코딩 방식
    5) 중복된 이름의 파일이 존재시 처리 정책 ( DefaultFileRenamePolicy 클래스 이용 )
    3-1. 프로젝트에 폴더를 만들어 거기에 집어넣을 경우 실제경로를 알아내 거기에 넣는다. request.getRealPath() 로 얻어내기.
    3-2. 폴더를 File 클래스의 mkdir() 메서드로 직접 만들 수 있으며 이 경로에 MultipartRequest 로 저장경로를 설정하여 저장할 수 있다.

    >> receive.jsp

    <%
    	String uploadPath = request.getRealPath("upload");
    	
    	System.out.println(uploadPath);
    	
    	String folderPath = "D:\\dojihye\\testFolder";
    	
    	File fileFolder = new File(folderPath);
    	
    	if (!fileFolder.exists()) {
    		try{
    			fileFolder.mkdir(); //폴더 생성합니다. make directory
    		    System.out.println("폴더가 생성되었습니다.");
    	        } 
    	        catch(Exception e){
    		    e.getStackTrace();
    		}        
             }else {
    		System.out.println("이미 폴더가 생성되어 있습니다.");
    	}
    	
    	try{
    		MultipartRequest 다중부분요청 = new MultipartRequest(request, 
    				folderPath,	// 파일저장 경로
    				1024*1204*5,// 최대용량
    				"utf-8",	// 업로드한 파일이 한글일 경우를 위해 인코딩
    				new DefaultFileRenamePolicy());
    		
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    	
    %>
    업로드한 파일 가져오기
    1. 해당 API 의 클래스인 MultipartRequest 를 사용해야 한다.
    2. 위처럼 파일을 폴더에 저장하기 위해 정보를 저장한 경우 getFile("input의name속성값"); 으로 얻어내기
    // input 태그의 name 속성이 매개값이니 업로드 되는 당시의 파일을 가져오는 것.
    3. 마찬가지로 request를 얻은 상태이니 MultipartRequest 도 getParameter("태그name속성값") 로 파일외의 다른 요소를 가져올 수 있다.
    	try{
    		MultipartRequest 다중부분요청 = new MultipartRequest(request, 
    				folderPath,	// 파일저장 경로
    				1024*1204*5,// 최대용량
    				"utf-8",	// 업로드한 파일이 한글일 경우를 위해 인코딩
    				new DefaultFileRenamePolicy());
    		
    		File file = 다중부분요청.getFile("profile");//MultipartRequest 가 request 에게 파일의 정보를 가져와있으니 얘한테 파일을 얻어내는것.
    		String name = 다중부분요청.getParameter("name");//파일이 아닌 텍스트부분도 request 를 가졌으니 가져올 수 있다.
    		System.out.println(name);
    		
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    업로드한 파일 폴더에서 지우기
    1. file 로 가져온 다음, File 클래스의 delete() 메서드를 사용하면 그 가져온 파일을 지운다.
    (이번에 input 에 올린 파일이니 결국 upload 되지 않고 파일은 더이상 생기지 않는다.)
    	try{
    		MultipartRequest 다중부분요청 = new MultipartRequest(request, 
    				folderPath,	// 파일저장 경로
    				1024*1204*5,// 최대용량
    				"utf-8",	// 업로드한 파일이 한글일 경우를 위해 인코딩
    				new DefaultFileRenamePolicy());
    		
    		File file = 다중부분요청.getFile("profile");//MultipartRequest 가 request 에게 파일의 정보를 가져와있으니 얘한테 파일을 얻어내는것.
    		file.delete();
    		
    	}catch(Exception e){
    		e.printStackTrace();
    	}

    업로드한 파일과 함께 회원table 에 넣어보기
    1. 다른건 그대로 동일. 파일은 자바에서 Blob(java.sql) 또는 byte[] 형 둘다 가능하다.
    Blob 는 byte의 모임이기 때문이다.
    2. PreparedStatement 에서 ? 에 인자를 넣을때도 setBlob() 또는 setBytes() 를 이용해 넣으면 된다.
    3. VO 에 값을 넣어줘야 이 메서드로 찾기 가능한데, File 객체였으니 byte배열로 바꾸는 작업이 필요하다.
    - 일단 file 크기만큼의 byte 배열을 만든다.
    - 파일입력스트림으로 파일을 읽도록 생성자 매개변수에 넣는다. ( byte 기준 )
    - 만들어놓은 file 크기의 byte 배열을 매개변수로 생성자에 넣었던 file 을 읽는다.
    (읽은 파일은 매개값인 byte 배열에 들어간다.)
    4. 다 끝나면 입력스트림도 close() 하고 file 도 지워준다.
    5. UploadDAO 의 메서드로 DB에 연결하여 작업을 수행한다.
    유의사항. 아랫단에서 MultipartRequest 를 만들었다고 윗단에서 request.getParameter() 사용 불가하다.(정보가 get이 안된다.)
    DAO의 메서드가 잘못됐을수도 있다.
    <form method=post> 가 되어야한다. file은 get방식으로 출력불가하다.(파일의 글자수는 많을거같다)
    <form enctype="multipart/form-data"> 여야 한다.

    >> UploadDAO.java

    	public void save(Upload upload) {
    		
    		try {
    			Class.forName("com.mysql.cj.jdbc.Driver");
    			Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/uploaddb1?useUnicode=true","root","1234");
    			PreparedStatement st = c.prepareStatement("insert into tblupload(name,profile,id,password) values(?,?,?,?)");
    			st.setString(1, upload.getName());
    			st.setBytes(2, upload.getProfile());
    			st.setString(3, upload.getId());
    			st.setString(4, upload.getPassword());
    			
    			st.executeUpdate();
    			
    			st.close();
    			c.close();
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    //
    	public Upload findByNo(int no) {
    		Upload upload = null;
    		
    		try {
    			Class.forName("com.mysql.cj.jdbc.Driver");
    			Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/tblupload?useUnicode=true","root","1234");
    			PreparedStatement st = c.prepareStatement("select * from tblupload where no = ?");
    			st.setInt(1, no);
    			
    			ResultSet set = st.executeQuery();
    			if(set.next()) {
    				upload = new Upload();
    				
    				upload.setName(set.getString("name"));
    				upload.setProfile(set.getBytes("profile"));
    				upload.setId(set.getString("id"));
    				upload.setPassword(set.getString("password"));
    			}
    			
    			st.close();
    			c.close();
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		
    		return upload;
    	}

    >> receive.jsp

    	String folderPath = "D:\\dojihye\\testFolder";
    		if (!fileFolder.exists()) {
    		try{
    			fileFolder.mkdir(); //폴더 생성합니다. make directory
    		    System.out.println("폴더가 생성되었습니다.");
    	        } 
    	        catch(Exception e){
    		    e.getStackTrace();
    		}        
             }else {
    		System.out.println("이미 폴더가 생성되어 있습니다.");
    	}
    
    	File fileFolder = new File(folderPath);
    
    	try{
    		MultipartRequest 다중부분요청 = new MultipartRequest(request, 
    				folderPath,	// 파일저장 경로
    				1024*1204*5,// 최대용량
    				"utf-8",	// 업로드한 파일이 한글일 경우를 위해 인코딩
    				new DefaultFileRenamePolicy());
    		
    		File file = 다중부분요청.getFile("profile");//MultipartRequest 가 request 에게 파일의 정보를 가져와있으니 얘한테 파일을 얻어내는것.
    		String name = 다중부분요청.getParameter("name");
    		String id = 다중부분요청.getParameter("id");
    		String password = 다중부분요청.getParameter("password");
    		
    		Upload upload = new Upload();
    		upload.setName(name);
    		upload.setId(id);
    		upload.setPassword(password);
    		
    		//file 에서 byte 배열로
    		byte[] profile = new byte[(int)file.length()];	//일단 file 크기만큼의 byte 배열을 만든다.
    		FileInputStream 입력스트림 = new FileInputStream(file);
    		입력스트림.read(profile);
    		
    		입력스트림.close();
    		file.delete();
    		
    		upload.setProfile(profile);
    		
    		UploadDAO dao = new UploadDAO();
    		dao.save(upload);
    		
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    업로드한 파일과 함께 회원정보 출력하기
    1. 회원을 DAO를 통해 찾고(회원번호매개로 찾는메서드) 그 회원의 profile 을 get한다.
    2. 응답객체의 contentType 을 image/jpeg 형식으로 지정한다.
    3. 지정한 형식으로 출력하기 위해 OutputStream 을 얻는다. (ServletOutputStream)
    4. 전달받은 OutputStream 이 형식에 맞춰 작성(write) 한다.
    결과값은 잘 나오나 계속 console창에 getOutputStream() 이 이미 호출되었다는 오류문구가 뜰 시
    <%@ page trimDirectiveWhitespaces="true" %>
    profile.jsp 윗단에 입력해주자. jsp 에서 나는 이상한 오류로 whitespace 로 인해 오류가 난 것이다. 이를 없애라는 명령어다.

    >> profile.jsp

    <%
    	// 바이너리로 응답 (html 응답) 모든 파일은 다 바이너리(byte)규격 이다.
    	int no = Integer.valueOf(request.getParameter("no"));
    	UploadDAO dao = new UploadDAO();
    	Upload upload = dao.findByNo(no);
    	
    	byte[] profile = upload.getProfile();
    
    	response.setContentType("image/jpeg");
    	ServletOutputStream os = response.getOutputStream();
    	os.write(profile);
    %>

    >> detail.jsp

    <body>
    <img src="profile.jsp?no=1" alt="" width="500px" height="500px"/>
    </body>
    웹브라우저는 contentType="text/html; 가 default 값으로 지정되어 있다.
    그래서 주소를 치면 기본적으로 html 을 먼저 찾는 것이다.
    그렇기 때문에 순수하게 파일이나 이미지를 출력할 경우 contentType 을 바꾸어 출력하면 된다.

    위의 profile.jsp 는 이제 contentType 이 image 이므로 detail.jsp 에서 해당주소로 image 의 src 지정이 가능하게 된 것이다.

    'JAVA' 카테고리의 다른 글

    댓글

Designed by Tistory.