본문 바로가기
Spring공부/3-파일업로드

파일업로드(3)-업로드데이터변환

by 으노으뇨 2021. 10. 13.
728x90
반응형
SMALL

첨부파일 데이터의 업로드가 완료되었지만, 아직도 많은 작업이 남아있다...

Ajax로 파일을 업로드해도 아직 브라우저 쪽에서 아무런 데이터도 전달하지 않았기 때문에 브라우저는

과 같은 오류만나오고 응답을 얻을 수 가 없다.

또는 파일을 올려놓아도

와같은 간단한 형식으로 "아 첨부가 되긴했구나"까지만 알뿐이다.

이것을 시각적으로나 정보를 포함시켜야한다.

그렇다면 브라우저로 전송해야하는 데이터는 어느것이 있을까?

1. 업로드 된 파일의 이름과 원본 파일의 이름

2. 파일이 저장된 경로

3. 업로드된 파일이 이미지인지 아닌지에 대한 정보

이에 대한 정보를 처리하는 방법은

1. 업로드된 경로가 포함된 파일이름을 반환하는 방식

2. 별도의 객체를 생성해서 처리하는 방법

이렇게 2가지가 있다. 

1번의 방식은 브라우저단에서 해야할 일이 많아지기 때문에 2의 방식으로 구성해보자.

우선 jackson-bind 라이브러리를 넣어주어야 한다.

Jackson 이란?

Java Object를 JSON으로 변환하거나 JSON을 Java Object로 변환하는데 사용할 수 있는 Java 라이브러리입니다.

Jackson Github - https://github.com/FasterXML/jackson

 Jackson 특징

1.Stream API : 스트림 형식으로 데이터를 분석하고 생성하기 때문에 성능이 좋습니다.

2.Tree Model : XML의 DOM 처럼 Node 형태로 데이터를 다룰 수 있기 때문에 유연성이 좋습니다.

3.Data Binding : POJO 기반의 자바 객체들을 JSON으로 변환시킬 수 있습니다.

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.5</version>
		</dependency>


		<dependency>
			<groupId>com.fasterxml.jackson.dataformat</groupId>
			<artifactId>jackson-dataformat-xml</artifactId>
			<version>2.9.5</version>
		</dependency>

AttachiFileDTO 클래스 생성

org.study.domain 패키지에 첨부파일의 정보들을 저장하는 AttachFileDTO 클래스를 작성한다.

package org.study.domain;

import lombok.Data;

@Data
public class AttachFileDTO {

	private String fileName;
	private String uploadPath;
	private String uuid;
	private boolean image;

}

AttachFileDTO 클래스에는 원본파일의 이름, 경로, UUID 값, 이미지 여부 정보를 하나로 묶어서 전달해야 하는 용도이다.

 

UploadController는 AttachFileDTO의 리스트를 반환하는 구조로 이제 변환해야한다.

좀복잡했다. 이 부분이

	@PostMapping(value = "/uploadAjaxAction", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResponseEntity<List<AttachFileDTO>> uploadAjaxPost(MultipartFile[] uploadFile) {

		List<AttachFileDTO> list = new ArrayList<>();
		String uploadFolder = "C:\\programing\\upload";

		String uploadFolderPath = getFolder();
		// make folder --------
		File uploadPath = new File(uploadFolder, uploadFolderPath);

		if (uploadPath.exists() == false) {
			uploadPath.mkdirs();
		}
		// make yyyy/MM/dd folder

		for (MultipartFile multipartFile : uploadFile) {

			AttachFileDTO attachDTO = new AttachFileDTO();

			String uploadFileName = multipartFile.getOriginalFilename();

			// IE has file path
			uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
			log.info("only file name: " + uploadFileName);
			attachDTO.setFileName(uploadFileName);

			UUID uuid = UUID.randomUUID();

			uploadFileName = uuid.toString() + "_" + uploadFileName;

			try {
				File saveFile = new File(uploadPath, uploadFileName);
				multipartFile.transferTo(saveFile);

				attachDTO.setUuid(uuid.toString());
				attachDTO.setUploadPath(uploadFolderPath);

				// check image type file
				if (checkImageType(saveFile)) {

					attachDTO.setImage(true);

					FileOutputStream thumbnail = new FileOutputStream(new File(uploadPath, "thumb_" + uploadFileName));

					Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 100, 100);

					thumbnail.close();
				}

				// add to List
				list.add(attachDTO);

			} catch (Exception e) {
				e.printStackTrace();
			}

		} // end for
		return new ResponseEntity<>(list, HttpStatus.OK);
	}

uploadAjaxPost()는 이제 기존과 달리 REsonsEntity<List<AttachFileDTO>> 를 반환하는 형태로 

수정되었고, JSON 데이터를 반환하도록 변경했다. 내부에서는 각 파일에 맞게

AttachFileDTO를 생성해서 전달하는 구조로 변경된것이다.

이제 브라우저에서 Ajax처리하도록 해보자.

앞서 얻은 데이터를 JS를 이용해서 반환된 정보를 처리하도록 해야한다.

upload.jsp파일의 Ajax부분을 조금 수정해주자

$.ajax({
					url : '/uploadAjaxAction',
					processData : false,
					contentType : false,
					data : formData,
					type : 'POST',
					dataType : 'json',
					success : function(result) {
						console.log(result);
					}
				});

Ajax를 호출했을 때의 결과 타입은 Json으로 변경하고, 결과를 콘솔로그로 찍도록 변경한것이다. 

첨부파일을 업로드 한 후에는 브라우저에서 결과를 아래와 같이 확일 할 수있다.

로그찍힌 것으로 현재 파일의 정보를 알수가있다. 이름, 이미지여부, 저장경로, UUID 등의 결과를 얻을 수 있따.

이제 다음에는 브라우저에서 썸네일을 처리해보자


브라우저에서 썸네일 처리

브라우저에서 첨부파일의 업로드 결과가 위와 같이 JSON객체로 반환되었다면, 남은 작업은 다음과 같다.

1. 업로드 후에 업로드 부분을 초기화 시키는 작업

2. 결과 데이터를 이용해서 화면에 썸네일이나 간략한 파일이미지를 보여주는 작업

현재 업로드는 <input type>을 통해서 이루어지기 때문에 한 번 업로드가 끝난 후에는 이를 초기화 시켜주는 작업과 업로드된 결과를 화면에 반영해 줄 필요가 있다.

<input type-'file'>의 초기화

이건 다른 DOM요소들과 다르게 readonly라서 안쪽의 내용을 수정할 수 없기 때문에 별도의 방법으로 초기화 시켜서 또 다른 첨부파일을 추가할 수 있도록 만들어보자

var cloneObj = $(".uploadDiv").clone();

그리고 ajax에도 이부분을 넣어준다

				$.ajax({
					url : '/uploadAjaxAction',
					processData : false,
					contentType : false,
					data : formData,
					type : 'POST',
					dataType : 'json',
					success : function(result) {

						console.log(result);

						$(".uploadDiv").html(cloneObj.html());

					}
				});

화면에서 첨부파일을 추가하고 버튼을 클릭하면 이전과 달리 첨부파일을 다시 추가할 수 있는 형태로 변경된다.

첫번째 업로드

업로드와 동시에 초기화 되었다.!!! 


업로드된 이미지 처리

업로드 된 결과는 Json 형태로 받아왔기 때문에 이를 이용해서 화면에 적정한 섬네일을 보여주거나 화면에 파일아이콘 등을 보여주어서 결과를 피드백 해줄 필요가 있다.

Ajax의 처리결과를 보여두록 수정한다.

upload.jsp 에 별도의 JS함수를 추가하여 특정한 <ul>태그 내에 업로드된 파일의 이름으 보여주도록 하겠다.

화면에는 <ul>태그를 작성해서 첨부파일 이름을 목록으로 처리 할 수 있도록 작성해준다.

	<div class='uploadDiv'>
		<input type='file' name='uploadFile' multiple>
	</div>
	<button id='uploadBtn'>Upload</button>
	<div class='uploadResult'>
		<ul>

		</ul>
	</div>

를 넣어준다. JS에서 처리된값을 넣을 컨테이너는 든든하게 준비되었다.

JS에서 목록을 보여주는 부분을 별도의 함수로 처리한다.

var uploadResult = $(".uploadResult ul");
			function showUploadedFile(uploadResultArr) {
				var str = "";
				$(uploadResultArr).each(function(i, obj) {
					str += "<li>" + obj.fileName + "</li>";
				});
				uploadResult.append(str);
			}

showUploadFile()은 Json데이터를 받아서 해당 파일의 이름을 추가한다. ajax결과에서 받은 json데이터를 showUploadFile()을 호출하도록 수정한다.

				$.ajax({
					url : '/uploadAjaxAction',
					processData : false,
					contentType : false,
					data : formData,
					type : 'POST',
					dataType : 'json',
					success : function(result) {

						console.log(result);

						showUploadedFile(result);
						$(".uploadDiv").html(cloneObj.html());

					}
				});

그럼 화면에는 어떻게 찍힐까?

화면에서는 업로드 후에 단순하게 업로드된 파일의 이름들이 보이는 것을 확인 할 수있다.

만약! 일반파일의 파일처리는 어떻게 해야할까?


일반파일의 파일 처리

첨부파일의 섬네일 이미지를 보여주는 작업은 조금 더 복잡하므로 우선적으로 일반 파일이 업로드된 상황에서 첨부파일의 아이콘들을 보여주도록 수정하자

기존의 webapp밑에 resources폴더의 내용을 그대로 추가하고, img폴더를 생성한다.

일반 첨부파일의 이미지를 보여줄 attach.png.파일을 추가한다.

이때 무료로 사용하는 것을 잊지말자^^

반드시 무료를 쓰도록하자..

첨부파일을 나타낼 파일을 저장했다.

upload.jsp파일에서 일반파일의 경우에는 attach.png이미지가 보이도록 수정한다.

			function showUploadedFile(uploadResultArr){
			    var str = "";
			    $(uploadResultArr).each(function(i, obj){
			      if(!obj.image){
			        str += "<li><img src='/resources/img/attach.png'>"+obj.fileName+"</li>";
			      }else{
			        str += "<li>"+ obj.fileName+"</li>";
			      }
			    });
			    uploadResult.append(str);
			  }

만약 obh.image 가 거짓이라면, 이미지 를 붙인다. 라는 것을 붙여서 보여준다. 한번 보도록 하자!

실험을 위해 txt파일을 하나더 만들어서 해보자

그랬더니 이미지가 아닌 파일은 위처럼 나왔다. 이미지파일은 섬네일을 보여주고 아닌경우 이제 저 아이콘을 보여주게 되는것이다.


썸네일 이미지 보여주기

일반 파일의 경우에는 단순히 파일 이미지만 보여주지만 이미지 파일의 경우에는 섬네일 파일을 보여주어야한다.

썸네일은 서버를 통해서 특정 URI 를 호출하면 보여줄수 있도록 처리하는데, 해당 파일의 경로와 UUID가 붙은 파일의 이름이 필요하므로 조금 복잡할 수있다.

서버에서 섬네일은 GET방식을 통해서 가져올 수 있도록 처리한다.

특정한 URI 뒤에 파일 이름을 추가하면 이미지파일 데이터를 가져와서 <IMG>태그를 장성하는 과정을 통해서 처리된다.

서버에 전송하는 데이터는 파일의 경로+thumb+UUID 가 붙은 파일이름 이다. 

그럼 UploadCOntroller에서 섬네일 데이터를 전송하도록 해보자

특정 파일이름을 받아서 이미지 데이터를 전송하는 코드를 우선 생성하자

	@GetMapping("/display")
	@ResponseBody
	public ResponseEntity<byte[]> getFile(String fileName) {

		log.info("fileName: " + fileName);

		File file = new File("C:\\programing\\upload\\" + fileName);

		log.info("file: " + file);

		ResponseEntity<byte[]> result = null;

		try {
			HttpHeaders header = new HttpHeaders();

			header.add("Content-Type", Files.probeContentType(file.toPath()));
			result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return result;
	}

코드에 대해 설명드리자면, 문자열로 파일의 경로가 포함된 fileName을 파라미터로 받고, byte[]를 전송한다.

byte[]로 이미지 파일의 데이터를 전송할 때 브라우저에 보내주는 MIME타입이 파일의 종류에 따라 달라지는 점이다.

이 부분을 해결하기 위해서 probeContentType()을 이용해서 적절한 MIME타입데이터를 Http의 헤더 메시지에 포함할 수있도록 처리한다.

총 파일 5개를 업로드하였다. 그럼 9개의 파일이 생겼다.

이중 UUID가 붙어있어 너무 지저분하니 몇개는 나는 UUID를 지워주고 실험하겠다.

http://localhost:8080/display?fileName=\2021\25\13\moong1.png

이때, \때문에 톰캣에서 에러를 불러일으킬 것이다. (유효하지않은 문자열 등)

그땐 이렇게 해결하자!

https://uno-kim.tistory.com/42

 

예외처리)유효하지않은 문자가 발견되었습니다.

파일업로드시 썸네일을 보여주는 단계의 실습도중 오류가 발생하였다. 파일경로를 주소창에 줄줄이 달아서 썻는데 오류가 났다. 나야 백단이나 JS단에서 오류가 났나부다 하고 대수롭지 않게

uno-kim.tistory.com

크게 UUID를 붙여도 상관없지만 너무 길어서 지워줬다.

위처럼 검색하니 정상적으로 나왔다!

그런데 이 작업중 오류가 생겼다. 톰캣에서 기타 특수문자를 처리를 못해주어서 유효하지않은 문자열에 대한 예외가 발생한것이다. 그것에 대해 처리한 글이 있으니 확인하자

유효하지않은 문자열처리 하는법

이로써 브라우저에서는 올바르게 이미지가 보이고, 파일의 확장자에 맞게 MIME타입이 변경되는 것을 볼 수있다.

브라우저에서 GET방식으로 첨부파일의 이름을 사용할 때에는 항상 파일 이름에 포함된 공백 문자나 한글 이름 등이 문제 될 수있다. 이를 수정하기 위해 encodeURIComponent()를 이용하자

URI호출에 적합한 문자열로 인코딩 처리하자

function showUploadedFile(uploadResultArr){
			    var str = "";
			    $(uploadResultArr).each(function(i, obj){
			    	 if(!obj.image){
				   var fileCallPath =  encodeURIComponent( obj.uploadPath+"/"+ obj.uuid +"_"+obj.fileName);
				   str += "<li><a href='/download?fileName="+fileCallPath+"'>" 
				     		  +"<img src='/resources/img/attach.png'>"+obj.fileName+"</a></li>"
				     }else{
				        var fileCallPath =  encodeURIComponent( obj.uploadPath+ "/s_"+obj.uuid +"_"+obj.fileName);
				       str += "<li><img src='/display?fileName="+fileCallPath+"'><li>";
				       }
			    });
			    uploadResult.append(str);
			  }

var fileCallPath = 에 encodeURIComponet를 추가해 주었다. 이제 정상적으로 한글파일로도 업로드가되며 썸네일이 표기되는지 확인해보자 그리고 이젠 멋진 사진으로 예시를 들어보자..

텍스트파일과 로고, 그리고 강아지 사진으로 진행해보았다.

총 3개의 파일이 정상적으로 입력되었다. 이제 썸네일이 정상적으로 올라나오는가?

정상적으로 올라왔다. 나는 썸네일만 썰렁하게 나오는게 좀 외로워보여서 이름도 함께 나오도록 한줄을 추가해주었다.

 str += "<li>" + obj.fileName + "</li>";

 

728x90
반응형
LIST

댓글