1. 사용할 Resolver 선택 및 등록
1.1. 필요한 jar 파일 추가

2. 폼 만들기

3. 모델 객체에 파일 다룰 속성 추가
3.1. String, bate[], MultupartFile 중 어떤 타입으로 받을 것인가 선택 및 코딩

4. 컨트롤러 만들기
(4.0. 바인딩 할 꺼면-String, byte[] 사용할 때- PropertyEditor 사용해서 바인딩 하기)
4.1. 디렉토리 설정
4.2. 디렉토리로 원본의 사본을 만들어 넣어 둡니다.

제 나름대로 책보고 예제 만들어 보다가 정리한 순서이지 꼭 저 위에 있는 순서대로 해야 하는건 아닙니다.

1. 사용할 Resolver 선택 및 등록

Cos와 Commons 라이브러리 중에 선택할 수 있습니다. Spring Reference, Pro Spring, Spring MVC 책 모두 Commons 라이브러리를 사용하고 있었습니다. 그래서 저도 그냥 Commons 라이브러리 사용했습니다.

1.1 필요한 Jar 파일 추가

Commons 라이브러리를 사용할 때 필요한 jar 파일은 두 개 입니다. 레퍼런스에는 commons-fileupload.jar 만 필요하다고 나오는데 저 jar 가 commons-io.jar 에 의존성을 가지고 있기 때문에 commons-io.jar도 추가해야 합니다.

2. 폼 만들기

간단합니다. <input type="file"> 로 해주면 파일 업로드 할 수 있는 버튼이 추가 됩니다. 이 때 폼태그에는 추가해줘야 할 것이 있습니다. request 타입을 multipart를 포함하는 request임을 알려주기 위한 것 같네요.

<form:form commandName="fileCommand" method="post" enctype="multipart/form-data">
    파일 선택 <input type="file" name="file"/><br/>
    <input type="submit" value="Submit"/>
</form:form>

3. 모델 객체에 파일 다룰 속성 추가

굳이 모델 객체에 추가하지 않고 커맨드 객체를 사용할 것이라면 커맨드 객체에 속성을 추가해 줍니다. 하지만 전 객체 하나 만드는 것 보다 필드 하나 추가하는 걸 선택했습니다. 따라서 FILE 도메인에 속성 하나를 추가할 것입니다.

3.1. String, bate[], MultupartFile 중 어떤 타입으로 받을 것인가 선택 및 코딩

폼에서 입력되는 파일을 객체의 속성으로 받기 위해 Spring에서 지원해주는 기능으로 사용할 수 있는 타입은 String, byte[], MultipartFile 세가지 입니다. 이 중에서 File 타입으로 받아서 File의 이름과 사이즈 등을 구하려면 역시 MultipartFile을 선택하는 것이 편하겠습니다.

    private String name;
    private Long size;
    private String filePath;
    private MultipartFile file;


4. 컨트롤러 만들기

4.0 String, bytep[], Multipart 로 바인딩 하기

Spring Reference 13.8.3에 보면 예제가 잘 나와있습니다. 저 세가지 중에서 String과 byte[]로 바인딩 할 때는 Spring이 제공하는 별도의 PropertyEditor를 등록해줘야 합니다. 하지만 MultipartFile은 그럴 필요가 없이 그냥 알아서 바인딩 해줍니다.

4.1. 디렉토리 설정

Spring 워크북에는 조금 다른 방식으로 등록했지만 저는 Spring MVC에 나와있는 예제가 사용한 방식을 사용했습니다.
컨트롤러에 File 타입으로 디렉토리를 속성으로 주고 setter injection을 사용합니다.

    <!-- MusicFile  -->
    <bean name="/fileUpload.html"
        class="com.classicMania.controller.file.UploadFileController">
        <property name="destinationDir" value="D://down" />
    </bean>

4.2. 디렉토리로 원본의 사본을 만들어 넣어 둡니다.

Spring이 제공하는 FileCopyUtils 를 사용하여 간단하게 원본(웹에서 입력하고 있는 파일)을 사본(위에서 설정한 디렉토리로)을 만들 수 있습니다.

컨트롤러의 소스코드를 보면 다음과 같습니다.

public class UploadFileController extends SimpleFormController implements InitializingBean {
    // 4.1
    private File destinationDir;

    public void setDestinationDir(File destinationDir) {
        this.destinationDir = destinationDir;
    }

    public UploadFileController() {
        setCommandName("fileCommand");
        setCommandClass(FILE.class);
        setFormView("file/uploadFile");
        setSuccessView("file/viewFileList");
    }

    public void afterPropertiesSet() throws Exception {
        if (destinationDir == null) {
            throw new IllegalArgumentException("Must specify destinationDir");
        }
        else if (!destinationDir.isDirectory() && !destinationDir.mkdir()) {
            throw new IllegalArgumentException(destinationDir + " is not a " + "directory, or it couldn't be created");
        }
    }

    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command,
            BindException exception) throws Exception {
        response.setContentType("text/plain");
        if (! (request instanceof MultipartHttpServletRequest)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");
        String fileName = file.getOriginalFilename();
        File destination = File.createTempFile("file", fileName, destinationDir);
        //4.2
        FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(destination));

        Member member = (Member) request.getSession().getAttribute("user");

        FILE newFile = (FILE) command;
        newFile.setMember(member);
        newFile.setFilePath(destination.getAbsolutePath());
        newFile.setName(file.getOriginalFilename());
        newFile.setSize(file.getSize());
        newFile.setFile(file);
        ServiceManager.getMemberService().update(member);
        ServiceManager.getFileService().add(newFile);

        Map<String, Object> model = new HashMap<String, Object>();
        model.put("files", ServiceManager.getFileService().getAll());
        model.put("owner", member.getName());
        return new ModelAndView(getSuccessView(), model);
    }

}

아~ Spring 좋아라~ 너무 좋아라~

파일 업로드 화면
사용자 삽입 이미지파일 선택 화면
사용자 삽입 이미지파일 업로드 선택
사용자 삽입 이미지파일 업로드 결과
사용자 삽입 이미지
위 컨트롤러에서 설정한 패스(D:\down)에 잘 들어갔습니다.