카테고리 없음

[Project][Spring] 이미지 파일 업로드 기능

321 2021. 4. 1. 23:15

pom.xml에 추가할 dependency

  • commons-fileupload
  • commons-io
<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.3.1</version>
</dependency>

<dependency> 
	<groupId>commons-io</groupId> 
	<artifactId>commons-io</artifactId> 
	<version>2.4</version> 
</dependency>



servlet-context.xml에 추가할 bean

  • multipartResolver - org.springframework.web.multipart.commons.CommonsMultipartResolver
  • property항목 : maxUploadSize, maxInMemorySize, defaultEncoding
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<beans:property name="maxUploadSize" value="52428800"/>
	<beans:property name="maxInMemorySize" value="1000000"/>
	<beans:property name="defaultEncoding" value="utf-8"/>
</beans:bean>

spring에서 지원하는 commons multipart resolver기능을 이용해 업로드 기능을 구현할 때 사용되는 프로퍼티를 추가해 준다.



ImageVO 클래스 작성

package kr.co.vo; 
import java.util.Date; 

public class ImageVO { 
	private String fileName; 
	private String uploadPath; 
	private String uuid; 
	private boolean isImage; 
	private int gno; 
	private String title; 
	private String content; 
	private String writer; 
	private Date bdate; 

	public ImageVO() { } 
	
    public ImageVO(String fileName, String uploadPath, String uuid, boolean isImage) { 
	super(); 
	this.fileName = fileName; 
	this.uploadPath = uploadPath; 
	this.uuid = uuid; 
	this.isImage = isImage; 
	} 

	public int getGno() { 
		return gno; 
	} 

	public void setGno(int gno) { 
		this.gno = gno; 
	} 

	public String getTitle() { 
		return title; 
	} 

	public void setTitle(String title) { 
		this.title = title; 
	} 

	public String getContent() { 
		return content; 
	} 

	public void setContent(String content) { 
		this.content = content; 
	} 

	public String getWriter() { 
		return writer; 
	} 

	public void setWriter(String writer) { 
		this.writer = writer; 
	} 

	public Date getBdate() { 
		return bdate; 
	} 

	public void setBdate(Date bdate) { 
		this.bdate = bdate; 
	} 

	public String getFileName() { 
		return fileName; 
	} 

	public void setFileName(String fileName) { 
		this.fileName = fileName; 
	} 

	public String getUploadPath() { 
		return uploadPath; 
	} 

	public void setUploadPath(String uploadPath) { 
		this.uploadPath = uploadPath; 
	} 

	public String getUuid() { 
		return uuid; 
	} 

	public void setUuid(String uuid) { 
		this.uuid = uuid; 
	} 

	public boolean isImage() { 
		return isImage; 
	} 

	public void setImage(boolean isImage) { 
		this.isImage = isImage; 
	} 

	@Override 
    public String toString() { 
		return "ImageVO [fileName=" + fileName + ", uploadPath=" + uploadPath + ", gno=" + gno + ", title=" + title + ", content=" + content + ", writer=" + writer + ", bdate=" + bdate + "]"; 
	} 
}



galleyMapper.xml

<?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="galleryMapper"> 
    <insert id="insert"> 
    <![CDATA[ 
    insert into myfile(gno, title, content, writer, filename, uploadpath) 
    values 
    (myfile_seq.nextVal, #{title}, #{content}, #{writer}, #{fileName}, #{uploadPath}) 
    ]]> 
    </insert> 
    
    <select id="select" resultType="kr.co.vo.ImageVO"> 
    <![CDATA[ 
    select gno, title, content, writer, bdate, filename, uploadpath 
    from 
    myfile 
    order by 
    gno desc ]]>
    </select>
</mapper>




GalleryDAO

package kr.co.dao; 
import java.util.List; 
import kr.co.vo.ImageVO; 
public interface GalleryDAO { 
    public void insert(ImageVO vo) throws Exception; 
    public List<ImageVO> select() throws Exception; 

}

 

GalleryDAOImpl

package kr.co.dao; 
import java.util.List; 
import java.util.Map; 
import org.apache.ibatis.session.SqlSession; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Repository; 
import kr.co.vo.ImageVO; 

@Repository 
public class GalleryDAOImpl implements GalleryDAO { 

    @Autowired 
    private SqlSession sqlSession; 

    @Override 
    public void insert(ImageVO vo) throws Exception { 
    sqlSession.insert("galleryMapper.insert", vo); 
    } 

    @Override public List<ImageVO> select() throws Exception { 
    return sqlSession.selectList("galleryMapper.select"); 
    } 

}




GalleryService

package kr.co.service; 
import java.util.List; 
import kr.co.vo.BoardVO; 
import kr.co.vo.ImageVO; 

public interface GalleryService { 
    public void insert(ImageVO vo) throws Exception; 
    public List<ImageVO> select() throws Exception; 
}

 

GalleryServiceImpl

package kr.co.service; 
import java.util.List; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
import kr.co.dao.GalleryDAO; 
import kr.co.vo.ImageVO; 

@Service 
public class GalleryServiceImpl implements GalleryService { 

    @Autowired 
    public GalleryDAO dao; 

    @Override public void insert(ImageVO vo) throws Exception { 
    dao.insert(vo); 
    } 

    @Override public List<ImageVO> select() throws Exception { 
    return dao.select(); 
    } 

}




GalleryController

  •  
package kr.co.controller; 
import java.io.File; 
import java.util.ArrayList; 
import java.util.UUID; 
import javax.inject.Inject; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.ResponseBody; 
import org.springframework.web.multipart.MultipartFile; 
import org.springframework.web.servlet.ModelAndView; 
import kr.co.service.GalleryService; 
import kr.co.util.GalleryUtils; 
import kr.co.vo.ImageVO; 
import net.coobird.thumbnailator.Thumbnails; 

@Controller 
@RequestMapping("/gallery/*") 
public class GalleryController { 

	private static final Logger logger = LoggerFactory.getLogger(GalleryController.class); 

    @Inject 
    public GalleryService service; 

    private static final String uploadPath = "C:\\Program Files\\sts-3.9.14.RELEASE\\workspace\\001MyProject\\src\\main\\webapp" + "\\WEB-INF\\resources\\uploadImage"; 
    private static final String thumbnailPath = "C:\\Program Files\\sts-3.9.14.RELEASE\\workspace\\001MyProject\\src\\main\\webapp" + "\\WEB-INF\\resources\\thumbnails"; 
    private static ArrayList<ImageVO> uploadList = new ArrayList<ImageVO>(); 

    @RequestMapping(value = "/list", method = RequestMethod.GET) 
        private ModelAndView thumbnailView(HttpServletResponse response) throws Exception { 
        ModelAndView mv = new ModelAndView(); 
        mv.addObject("list", service.select()); 
        mv.setViewName("gallery/list"); 
        return mv; 
    } 

    @RequestMapping(value = "/write", method = RequestMethod.GET) 
        public String write() throws Exception { 
        return "gallery/write"; 
    } 

    @ResponseBody 
    @RequestMapping(value = "/upload", method = RequestMethod.POST) 
    public ArrayList<ImageVO> uploadAjax(@RequestParam MultipartFile[] uploadFile) throws Exception { 
        File uploadFolder = GalleryUtils.makeFolder(uploadPath); 
        for (MultipartFile multipartFile : uploadFile) { 
            String fileName = multipartFile.getOriginalFilename(); 
            logger.info("name " + fileName + " size " + multipartFile.getSize()); 
            String uuid = UUID.randomUUID().toString(); 
            fileName = uuid + "_" + fileName; 
            File saveFile = new File(uploadFolder, fileName); 
            ImageVO vo = new ImageVO(fileName, uploadFolder.toString(), uuid, true); 
            if (GalleryUtils.checkImage(saveFile)) { 
            uploadList.add(vo); 
            multipartFile.transferTo(saveFile); 
            } 

            if(saveFile.getName().equals(uploadList.get(0).getFileName())) { 
                String thumbnailName="thumbnail_"+uploadList.get(0).getFileName(); 
                File thumbnailFile = new File(thumbnailPath, thumbnailName); 
                Thumbnails.of(saveFile).size(250, 250).toFile(thumbnailFile); 
            } 
		} 
		return uploadList; 
	} 

    @RequestMapping(value = "/uploadContent", method = RequestMethod.POST) 
    public String send(HttpServletRequest request) throws Exception { 
        ImageVO uploadVO = new ImageVO(); 
        uploadVO.setFileName(uploadList.get(0).getFileName()); 
        uploadVO.setUploadPath(uploadList.get(0).getUploadPath()); 
        logger.info("---------send success-------------"); 
        String title = (String) request.getParameter("title"); 
        String content = (String) request.getParameter("content"); 
        uploadVO.setTitle(title); 
        uploadVO.setContent(content); 
        uploadVO.setWriter("글쓴ㅇㅣ"); 
        logger.info(title + content + uploadList.toString()); 
        service.insert(uploadVO); return "redirect:/gallery/list"; 
    } 
}

 

  • upload(이미지업로드)
    • 1. uploadPath라는 경로에 폴더를 하나 만들고
    • 2. upload 할 다중파일을 가져와서
    • 3. 원래 이름과 새로 만든 랜덤아이디(uuid)를 합쳐서 파일이름을 만들어준다.
    • 4. 파일을 saveFile이라는 이름으로 새로 정의하면서 3의 새로 만든 이름과 1번에서 만든 폴더에 만들어주고
    • 5. 이것들을 VO에 담아서(이미지인지 체크하고 맞으면) uploadList에 담아준다.
    • 6. 4번의 파일은 그냥 파일껍데기였으므로 아까 받아온 multipartFile을 transfer 해서 담아준다.
    • 7. 계속 for문이 돌아가고 있는 상태이므로 첫 번째 파일만 썸네일로 만들고 싶어서 저장한 파일의 이름이 uploadList의 첫 번째인지 확인하고 맞다면 썸네일을 만들어준다.
    • 8. 썸네일파일 이름과 파일은 saveFile과 비슷한 방법으로 만들어주지만 Thumbnails.of(파일이름). size(가로, 세로). toFile(썸파일껍데기); -> 썸네일 파일 사진 크기를 만들어주는 것.
  • uploadContent(글 업로드)
    • 위에서 업로드한 이미지를 글이랑 같이 올려야 하므로 jsp에서 글제목, 글내용, 글쓴이 등의 데이터를 받아와서 업로드한 파일의 첫 번째 이름, 경로와 함께 DB에 등록한다.




GalleryUtils

  • 컨트롤러에서 기능하는 세세한 것들을 밖으로 빼서 코드가 너무 길어지거나 복잡해지지 않게 함
package kr.co.util; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.nio.file.Files; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.web.multipart.MultipartFile; 
import kr.co.controller.GalleryController; 
import net.coobird.thumbnailator.Thumbnailator; 

public class GalleryUtils { 
    private static final Logger logger = LoggerFactory.getLogger(GalleryController.class); 
    private static int folderNameIndex = 0; 
    
    public static String getFolderName() { 
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
        String folderName = sdf.format(new Date()); 
        folderName = folderName.replace("-", File.separator) +"_" + folderNameIndex; 
    	return folderName; 
    } 

    public static File makeFolder(String uploadPath) { 
        logger.info(GalleryUtils.getFolderName()); 
        File uploadFolder = new File(uploadPath, getFolderName()); 
        if (!uploadFolder.exists()) { 
        	uploadFolder.mkdirs(); folderNameIndex++; 
        } 
        return uploadFolder; 
    } 

    public static void makeThumbnail(MultipartFile multipartFile, String thumbnailPath, String fileName) throws Exception { 

        FileOutputStream thumbnail = new FileOutputStream(new File(thumbnailPath, "thumbnail_" + fileName)); 
        Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 2048, 2048); 
        thumbnail.write(200); 
        thumbnail.close(); 
    } 

    public static boolean checkImage(File file) throws Exception { 
        boolean returnValue = false; 
        String type = Files.probeContentType(file.toPath()); 
        if (type.startsWith("image")) { 
        	returnValue = true; 
        } 
        return returnValue; 
    } 
}




todo


  • 원본파일과 썸파일을 분리해야 할까?
  • uploadContent의 setFileName을 설정할 때 썸네일파일만 올라가나? 2개 이상의 파일이름을 db에 담아줘야 하지 않을까? 경로만 저장해도 괜찮을까?
  • 글쓴이 부분 로그인하면 바뀌도록 설정.
  • upload주소를 /uploadImage로 바꾸면 더 명확해질 것