File Upload

  • 파일명과 경로를 문자열 형태로 넘겨주어 저장 및 출력할 경우, 해당 파일을 갖고 있지 않은 단말기에서 코드를 실행 시 파일 추적 불가
  • 가상 경로를 생성하여 외장 서버 저장소에 저장해야 서버 사용자 모두가 파일 사용 가능
  • 이를 위해 별도의 작업 필요

Upload Single File

  • <form>을 통해 값을 전달 시, 단순한 입력 값과 달리 파일을 업로드하기 위해서는 선행 조건이 필요
<!--pom.xml-->
<dependencies>
	<!-- add 파일업로드2개 -->
		<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
		<dependency>
		    <groupId>commons-io</groupId>
		    <artifactId>commons-io</artifactId>
		    <version>2.4</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
		<dependency>
		    <groupId>commons-fileupload</groupId>
		    <artifactId>commons-fileupload</artifactId>
		    <version>1.3.1</version>
		</dependency>
</dependencies>
  • mvnrepository.com에서 commons-io와 commons-fileupload를 위한 <dependency>를 복사
  • pom.xml 파일의 <dependencies> 내에 추가
<!--jsp파일-->
<body>
	<form action="upload1" method="post" enctype="multipart/form-data">
		<table>
			<caption align="top"><b>스프링 업로드(파일1개)</b></caption>
			<tr>
				<th>파일업로드</th>
				<td><input type="file" name="photo" class="form-control"></td>
			</tr>
			<tr>
				<td colspan="2" align="center">
					<button type="submit">업로드</button>
				</td>
			</tr>
		</table>
	</form>
</body>
  • 단순 입력 값만 전달할 때와 달리 반드시 ‘enctype=”multipart/form-data”’ 추가
//Controller
@Controller
@RequestMapping("/test")
public class Controller {

	@PostMapping("/upload1")
	public ModelAndView read1(
			@RequestParam MultipartFile photo,
			/*HttpServletRequest request*/
			HttpSession session) { //realPath(업로드할 실제경로) 찾기 위해서
		
		ModelAndView model=new ModelAndView();
		model.setViewName("upload/uploadResult1");
		
		//업로드할 실제경로 구하기
		String path=session.getServletContext().getRealPath("/WEB-INF/image");
		String fileName=photo.getOriginalFilename(); //업로드된 파일명
		
		//현재날짜와 시간이용해서 파일명에 저장(중복방지..이름다르게)
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
		
		if(!fileName.equals("")) {
			
			fileName=sdf.format(new Date())+"_"+fileName;
			
			//path에 업로드
			try {
				photo.transferTo(new File(path+"\\"+fileName));
			}
			catch (IllegalStateException e) {	e.printStackTrace(); }
			catch (IOException e) {	e.printStackTrace(); }
		} else {fileName="no";} //업로드안할경우

		model.addObject("fileName", fileName);
		model.addObject("title", title);
		model.addObject("path", path);
		
		return model;
	}
}
  • 매핑 메서드의 인자로 반드시 필요한 것은 다음과 같다
    • multipart/form-data로 업로드한 파일의 정보를 포함하는 MultipartFile 객체 (변수 photo)
    • 실제 저장되는 경로(realPath)의 정보를 포함하는 HttpServletRequest 혹은 HttpSession 객체 (HttpServletRequest 객체의 getSession() 메서드를 통해 HttpSession 객체를 호출할 수 있으므로 임의로 사용 가능)
  • 실제로 업로드하여 저장되는 경로는 다음의 방법으로 구할 수 있다
    • session.getServletContext().getRealPath("/WEB-INF/image");
    • getRealPath() 메서드의 인자 값으로는 업로드한 파일 저장 용도의 가상 경로를 만들기 위한 실제 파일의 절대 경로를 입력
  • 실제로 업로드한 파일이 가지는 파일명은 다음의 방법으로 구할 수 있다
    • MultipartFile 객체(photo)의 getOriginalFilename() 메서드는 실제 저장되는 파일명을 반환
    • 그러나 JSP에서 ‘DefaultFileRenamePolicy()’와 같이 하나의 파일이 중복 저장될 경우 이름을 달리 하여 다중 저장되는 기능은 없음
  • 중복 저장이 불가한 문제를 해결하기 위해 모든 저장되는 파일명에 업로드 시간을 표기하여 중복 방지
    • SimpleDateFormat 객체를 이용하여 시, 분, 초까지 기록
    • fileName=sdf.format(new Date())+"_"+fileName’와 같이 업로드 시간을 파일명에 삽입
  • 실제 저장될 경로에 업로드하는 방법은 다음과 같다
    • MultipartFile 객체(photo)의 transferTo(new File()) 메서드가 파일을 실질적으로 업로드하는 기능
    • transferTo() 객체의 인자인 new File() 메서드의 인자 값으로는 업로드한 파일까지의 저장 경로(저장 경로 + 파일명) 입력
  • 업로드한 파일이 없을 경우의 예외 처리 (try ~ catch)
<body>
	<h2>업로드한 실제경로: ${path }</h2>
	<h2>업로드한 이미지명: ${fileName }</h2>
	
	<c:if test="${fileName=='no' }">
		<img src="../photo/noimage.png" style="max-width: 100px">
	</c:if>
	<c:if test="${fileName!='no' }">
		<img src="../photo/${fileName }" style="max-width: 100px">
	</c:if>
</body>
  • 저장된 파일을 찾기 위해서는 실제 저장 경로인 ${path}의 출력 값을 통해야 함
  • 업로드한 파일이 없을 경우를 위한 조건문 작성

Upload Multiple Files

<!--view-->
<body>
	<form action="upload2" method="post" enctype="multipart/form-data">
		<table>
			<caption align="top"><b>스프링 업로드(파일여러개)</b></caption>
			<tr>
				<th>업로드</th>
				<td><input type="file" name="photo" class="form-control" multiple="multiple"></td>
			</tr>
			<tr>
				<td colspan="2" align="center">
					<button type="submit">업로드</button>
				</td>
			</tr>
		</table>
	</form>
</body>
  • 단일 파일 업로드 시와 달라진 점은 <input type=”file”>에 ‘multiple=”multiple”’ 속성이 추가
//Controller
@Controller
@RequestMapping("/test")
public class Controller {

	@PostMapping("/upload2")
	public ModelAndView read2(@RequestParam String title,
			@RequestParam ArrayList<MultipartFile> photo,
			HttpSession session) {
		
		ModelAndView model=new ModelAndView();
		model.setViewName("upload/uploadResult2");
		
		//업로드할 실제경로 구하기
		String path=session.getServletContext().getRealPath("/WEB-INF/image");

		//현재날짜와 시간이용해서 파일명에 저장(중복방지..이름다르게)
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
		
		ArrayList<String> fileNames=new ArrayList<String>();
		
		//파일명담기
		for(MultipartFile f:photo) {
			
			String fileName=sdf.format(new Date())+"_"+f.getOriginalFilename();
			fileNames.add(fileName);
			
			//실제업로드
			try {
				f.transferTo(new File(path+"\\"+fileName));
			}
			catch (IllegalStateException e) {	e.printStackTrace(); }
			catch (IOException e) { e.printStackTrace(); }
		}

		model.addObject("fileNames", fileNames);
		model.addObject("title", title);
		model.addObject("path", path);
		
		return model;
	}
}
  • 단일 파일 업로드 시와 Controller도 유사
  • 하지만 여러 파일이 업로드 되었으므로 다음의 작업을 반복문을 통해 반복해야 함
    • transferTo(new File()) 객체를 통한 실제 파일 업로드
    • 필요에 따라 List<String> 객체에, 출력을 위한 실제 저장 경로 및 파일명 저장
<!--view-->
<body>
	<h2>업로드한 실제경로: ${path }</h2>
	<h2>업로드한 이미지들</h2>
	
	<c:forEach var="imgs" items="${fileNames }">
		<img src="../photo/${imgs }">
	</c:forEach>
</body>
  • 업로드한 이미지가 여러 개이므로 반복문 <c:forEach>를 이용하여 모두 출력 가능

JDBC_Connect to DataBases

Configurations

<!--pom.xml-->
<dependencies>
	<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>8.0.30</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.5.9</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis-spring</artifactId>
		    <version>2.0.7</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>${org.springframework-version}</version>
		</dependency>
</dependencies>
  • mvnrepository.com에서 mysql-connector-java, mybatis, mybatis-spring, spring-jdbc를 위한 <dependency>를 복사
  • pom.xml 파일의 <dependencies> 내에 추가

JDBC

<?xml version="1.0" encoding="UTF-8"?>
<beans>
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="사용자 드라이버 클래스명"></property>
		<property name="url" value="사용자 db url"></property>
		<property name="username" value="사용자 아이디"></property>
		<property name="password" value="사용자 비밀번호"></property>
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="configLocation" value="classpath:mybatis-config.xml"/>
		<property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/>
	</bean>
	
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactory"/>
	</bean>
	
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
</beans>
  • class가 DriverManagerDataSource인 <bean> 생성 후 4개의 각 <property>에 해당하는 value 지정
  • #sqlSessionFactory <bean>에 입력
    • <property name="configLocation" value="classpath:mybatis-config.xml"/> (src/main/resources>mybatis-config.xml : 멤버 변수의 자료형 지정을 위한 mybatis 설정 파일)
    • <property name="mapperLocations" value="classpath:mapper/Mapper.xml"/> (src/main/resources>mapper>파일들 : DAO에서 사용할 SQL문 지정을 위한 mapper 설정 파일, 와일드카드()를 통해 여러 파일을 동시에 지정 가능)
  • #sqlSession <bean>은 DAO(@Repository 등록)와 SQL Mapper를 연결하는 기능

MyBatis

<!--mybatis-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<typeAlias alias="mdto" type="spring.mysql.mycar.MyCarDto"/>
	</typeAliases>
</configuration>
  • doctype을 위와 같이 작성
  • <configuration>의 <typeAliases>태그를 통해 자료형이 될 객체(멤버 변수 포함한 객체)의 별칭(alias)를 지정 가능 → 이는 Mapper에서 활용

Mapper

<!--Mapper-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="spring.mysql.mycar.MyCarDao"> <!-- 임의지정 가능 -->
	<!-- 전체리스트 -->
	<select id="getAllListMyCar" resultType="mdto"> <!-- resultType은 반환자료형(dto는 경로까지..alias설정) -->
		select * from mycar order by num desc
	</select>
	
	<!-- 전체개수얻기 -->
	<select id="getTotalCount" resultType="int">
		select count(*) from mycar
	</select>
	
	<!-- insert -->
	<insert id="insertOfMyCar" parameterType="mdto">
		insert into mycar (carname,carprice,carcolor,carguip) values(#{carname},#{carprice},#{carcolor},#{carguip})
	</insert>
	
	<!-- delete -->
	<delete id="deleteOfMyCar" parameterType="String">
		delete from mycar where num=#{num}
	</delete>
	
	<!-- update -->
	<update id="updateOfMyCar" parameterType="mdto">
		update mycar set carname=#{carname},carprice=#{carprice},carcolor=#{carcolor},carguip=#{carguip} where num=#{num}
	</update>
</mapper>
  • doctype을 위와 같이 작성
  • <mapper>의 namespace는 DAO에서 사용할 메서드의 범위 탐색 시 활용할 변수명 입력
  • 기능에 따라 <select>, <insert> 등의 태그 사용
    • id 속성은 DAO에서 사용할 작업을 호출하기 위한 이름 (java의 메서드 기능)
    • resultType 속성은 SQL문의 결과로 발생한 값들의 자료형(혹은 멤버 변수를 포함한 객체)
    • parameterType 속성은 SQL문에 대입할 인자 값의 자료형(혹은 멤버 변수를 포함한 객체)
    • 입력된 인자 값은 ‘#{ 변수명 }’의 방식으로 활용

DTO & DAO

public class MyCarDto {
	//dto는 기존과 마찬가지로 private 멤버변수와 getter,setter 포함 }
//dao
@Repository
public class MyCarDao {

	@Autowired
	private SqlSession session;
	
	String namespace="spring.mysql.mycar.MyCarDao";
	
	public int getTotalCount() {
		return session.selectOne(namespace+".getTotalCount");
	}
	
	public void insertCar(MyCarDto dto) {
		session.insert("insertOfMyCar", dto);
	}
	
	public List<MyCarDto> getAllCars() {
		return session.selectList("getAllListMyCar");
	}
	
	public void deleteCar(String num) {
		session.delete("deleteOfMyCar", num);
	}
}
  • DAO 클래스는 @Repository 어노테이션으로 Beans에 등록
  • DAO를 SQL문을 정의한 Mapper와 연결하기 위해 SqlSession 객체 생성 후 이를 @Autowired
  • SqlSession을 통해 SQL문에 접근하기 위한 방법은 다음과 같다
    • 기능에 따라 SqlSession 객체(session)의 select*(), insert() 등의 메서드 호출
    • select*() 메서드는 단일 객체 반환 시 selectOne(), 복수 객체 반환 시 selectList() 사용
    • 메서드에 입력된 인자 값은 SqlSession 객체의 인자 값으로 그대로 입력하여 SQL에 전달
    • Mapper의 해당 태그를 찾기 위해 정확한 id 값 입력 (중복 id가 있다면 namespace 속성을 지정하여 범위 지정 가능 : “mycar.getData” 혹은 namespace+”.getData”)

Controller

@Controller
public class CarController {

	@Autowired
	MyCarDao dao;
	
	@GetMapping("/kakao/list")
	public String list(Model model) {
		
		//dao로부터 총개수가져오기
		int totalCount=dao.getTotalCount();
		
		//목록 가져오기
		List<MyCarDto> list=dao.getAllCars();
		
		//request에 저장
		model.addAttribute("totalCount", totalCount);
		model.addAttribute("list", list);
		
		return "car/carList";
	}
	
	@PostMapping("/kakao/read")
	public String carinsert(@ModelAttribute("dto") MyCarDto dto) {
		
		dao.insertCar(dto);
		
		return "redirect:list"; //redirect시에는 중간경로 빼고 마지막 경로만
	}
}
  • DAO(@Repository)와 @Autowired로 연결하여 멤버 객체 호출
  • 매핑을 통해 가상 경로 지정 및 값 전달
  • 호출한 DAO 객체를 통해 필요한 메서드 사용 가능
    • select의 경우 반환 값을 활용
    • void 타입 메서드의 경우 DAO 객체 호출하여 사용, 반환 값은 없음
  • 하나의 절대 경로에는 하나의 매핑 주소만 지정 가능 (중복 시 redirect 이용)
    • redirect는 일반적으로 매핑 주소의 중간 경로를 제외한 마지막 경로만 지정

View

//form에서 값 입력 및 전달, 출력에서 값 호출은 기존과 같은 방법 사용
profile
초보개발자

0개의 댓글