이로

HTML IMG 태그에 경로없이 이미지 전송 (HttpServletResponse 사용) 본문

컴퓨터/개발일기

HTML IMG 태그에 경로없이 이미지 전송 (HttpServletResponse 사용)

利路 2021. 1. 4. 21:04
반응형

이슈사항

  • HTML 에서 IMG 태그 안 SRC 에 이미지에 대한 경로를 노출하지 않고 이미지 파일을 노출시키고 싶다.
  • 성능이 조금 떨어지더라도 보안이 중요하다.

 

구성

  • springboot 2.2.6
  • JSP, JQuery, JSTL

 

해결방안

  1. HttpServletResponse를 사용하여 response에 image파일을 담아 반환한다.
  2. 파일경로를 암호화, 복호화 하여 사용한다.
  3. 이미지파일을 base64로 읽어 ModelAndView 형태로 JSP에 전송하여 출력

 

HttpServletResponse사용하여 이미지 파일 전송 로직

  1. HTML <IMG SRC = ""/> 에서 SRC값으로 IMG파일을 받아올 URL 을 넣는다.
    • 특정 DATA 를 가져오기 위해 GET 방식으로 DATA를 호출할 수 있도록 한다.
  2. Controller에서 image file에 대한 정보를 불러오기 위해 FileVO를, 클라이언트의 브라우저 확인을 위해 request를, response에 IMG 파일을 담아 전송하기 위해 response를 커맨드 객체로 선언하고 fileService의 getImgFile을 호출한다.
  3. Service에서 fileInfo에 있는 이미지 파일에 대한 정보를 가지고 이미지 파일의 상세정보를 호출한뒤, fileFullpath 와 file의 원본파일명으로 이미지 파일을 가져올 준비를 한다. 그 다음 response객체의 헤더에 이미지 파일에 대한 정보를 담아주고, stream 을 통해 이미지 파일을 전송한다.

 

코드

1. HTML

<img src="<c:url value='/image?imageId=${imageId}'/>">

 

2. 컨트롤러

@RequestMapping(value = "/image",method = RequestMethod.GET)
	public void getImgFile(FileVO fileVO, HttpServletRequest request, HttpServletResponse response) throws Exception {
		fileService.getImgFile(fileVO,request, response);
}

 

3. 서비스 (FileService.class)

	public void getImgFile(FileVO fileVO, HttpServletRequest request, HttpServletResponse response) throws Exception {
		fileVO = fileService.selectFileInfo(fileVO);
		FileComponent.getImgFile(request, response, fileVO.getFileFullPath(), fileVO.getOriginName());
	}

 

4. 파일을 Response 객체에 담는 로직

public static void getImgFile(HttpServletRequest request, HttpServletResponse response, String fullFilePath, String original) throws Exception {
		File file = new File(fullFilePath);
		
		if (!file.exists()) {
			throw new FileNotFoundException(fullFilePath);
		}
		
		if (!file.isFile()) {
			throw new FileNotFoundException(fullFilePath);
		}
		// original : 원본 파일명
		String extend = original.split("\\.")[1];
		original = original.replaceAll("\r", "").replaceAll("\n", "");
		response.setContentType("image/"+extend);
		response.setHeader("Content-Transfer-Encoding", "binary");
		response.setHeader("Pragma", "no-cache");
		response.setHeader("Expires", "0");
		
		setDisposition(original, request, response);
		
		BufferedInputStream fin = null;
		BufferedOutputStream outs = null;
		
		byte[] byteImgArray = new byte[(int) file.length()];
		
		try {
			fin = new BufferedInputStream(new FileInputStream(file));
			outs = new BufferedOutputStream(response.getOutputStream());
			
			int read = 0;
			
			while ((read = fin.read(byteImgArray)) != -1) {
				outs.write(byteImgArray, 0, read);
			}
		    
		} finally {
			fin.close();
			outs.close();
		}
	}

        /**
	 * Disposition 지정하기.
	 *
	 * @param filename 파일명
	 * @param request 브라우저의 종류를 얻어온다.
	 * @param response Disposition을 지정해 Header를 설정한다.
	 * @throws Exception Throwble Unhandled Exception
	 */
	private static void setDisposition(String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {
		String browser = getBrowser(request);

		String dispositionPrefix = "attachment; filename=";
		String encodedFilename = null;

		if (browser.equals("MSIE")) {
			encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
		} else if (browser.equals("Trident")) { // IE11 문자열 깨짐 방지
			encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
		} else if (browser.equals("Firefox")) {
			encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
		} else if (browser.equals("Opera")) {
			encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
		} else if (browser.equals("Chrome")) {
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < filename.length(); i++) {
				char c = filename.charAt(i);
				if (c > '~') {
					sb.append(URLEncoder.encode("" + c, "UTF-8"));
				} else {
					sb.append(c);
				}
			}
			encodedFilename = sb.toString();
		} else {
			throw new IOException("Not supported browser");
		}

		response.setHeader("Content-Disposition", dispositionPrefix + encodedFilename);

		if ("Opera".equals(browser)) {
			response.setContentType("application/octet-stream;charset=UTF-8");
		}
	}

 

반응형

'컴퓨터 > 개발일기' 카테고리의 다른 글

201222 쓰레드 다중처리  (0) 2020.12.22
Comments