개발자의 길

[java] url 파일 다운로드 / 서버 파일 경로 다운로드 본문

4. JAVA

[java] url 파일 다운로드 / 서버 파일 경로 다운로드

자르르 2024. 1. 5. 17:56


java 든 spring 이든

 

파일 다운로드를 바로 시키는 경우가 아닌,

 

pk값을 가지고 db에서 파일 정보를 조회해 와서 다운로드 시키는 방식을 많이 사용한다.

 

이럴 경우 2가지 케이스가 존재한다.

 

1. 실제 파일 경로

ex)

 - /home/my/test.png

 - C:\Desktop\test.zip

 

2. url 경로

 - http://test.test.com/test.png   

 - https://test.test.com/aaa.zip

 

 

실제 파일이 저장된 경로를 가지고 있든, 브라우저에서 호출되는 url 경로를 가지고 있는 2가지 이다.

 

2가지 케이스에 대한 예제를 남긴다.

 

1번 케이스 (file path)

 - FileDownloadResponse.java

package com.test.common.response;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;

/**
 * 파일 다운로드 뷰입니다.
 * @author 비밀
 */
public final class FileDownloadResponse implements View {
	private String contentType = "application/download; utf-8";
	private String filePath;
	private String fileName;

	public FileDownloadResponse(String filePath) {
		this(filePath, null);
	}

	public FileDownloadResponse(String filePath, String fileName) {
		this.filePath = filePath;
		this.fileName = fileName;
	}

	@Override
	public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		final File downloadFile = new File(filePath);

		/* fileName은 있으면 그 이름으로, 없으면 파일로 부터 추출해서! */
		if (fileName == null) {
			fileName = downloadFile.getName();
		}

		response.setContentType(getContentType());
		response.setHeader("Content-Disposition", "attachment;filename=\"" + new String(fileName.getBytes("euc-kr"), "8859_1") + "\"");
		response.setHeader("Content-Transfer-Encoding", "binary");

		FileCopyUtils.copy(new FileInputStream(downloadFile), response.getOutputStream());
	}

	public String getFilePath() {
		return filePath;
	}

	public String getFileName() {
		return fileName;
	}

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

	@Override
	public String getContentType() {
		return this.contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}
}

 

java에서 실행 방법

public class Downloadtest {
	public View getFileDownload() {
        String fullPath="C:\Desktop\test.zip";
        String fileNm = "change_test.zip";
        return new FileDownloadResponse(fullPath, fileNm);
    }
}

 

2번 케이스 (url)

 - FileUrlDownloadResponse.java

package com.test.common.response;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.View;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * 파일 다운로드 뷰입니다.
 * @author 뷔밀
 */
public final class FileUrlDownloadResponse implements View {
	private String contentType = "application/download; utf-8";
	private String fileUrl;
	private String fileName;

	public FileUrlDownloadResponse(String fileUrl) {
		this(fileUrl, null);
	}

	public FileUrlDownloadResponse(String fileUrl, String fileName) {
		this.fileUrl = fileUrl;
		this.fileName = fileName;
	}

	@Override
	public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		/* fileName은 있으면 그 이름으로, 없으면 파일로 부터 추출해서! */
		if (fileName == null) {
			String fileNameExtension = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
			fileName = fileNameExtension;
		}

		InputStream in = null;
		try {
			try {
				in = URI.create(fileUrl).toURL().openStream();
			} catch (SSLHandshakeException e) {
				//이건 임시로 ssl 인증서 오류를 통과 시키는 소스다 https://test.co.kr/img.png 같은 경우는 실제로 인증서가 없는 도메인이라 https가 오류..
				if(fileUrl.startsWith("https://")) {
					this.disableSslVerification();
					in = URI.create(fileUrl).toURL().openStream();
				} else {
					throw new FileNotFoundException();
				}
			}
			response.setContentType(getContentType());
			response.setHeader("Content-Disposition", "attachment;filename=\"" + new String(fileName.getBytes("euc-kr"), "8859_1") + "\"");
			response.setHeader("Content-Transfer-Encoding", "binary");

			FileCopyUtils.copy(in, response.getOutputStream());
		}finally {
			if(in !=null) {
				try{in.close();}catch (Exception e){}
			}
		}
		//아래 소스가 최신 소스지만, ssl 오류 대응을 못해서, 옛날 방식으로
//		try(InputStream in = URI.create(fileUrl).toURL().openStream()){
//			FileCopyUtils.copy(in, response.getOutputStream());
//		}

	}

	public String getFileUrl() {
		return fileUrl;
	}

	public String getFileName() {
		return fileName;
	}

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

	@Override
	public String getContentType() {
		return this.contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	//인증서가 제대로 적용이 안된 ssl(https) 호출시에 통과 되도록 하는 임시 소스
	private void disableSslVerification() {
		// TODO Auto-generated method stub
		try {
			// Create a trust manager that does not validate certificate chains
			TrustManager[] trustAllCerts = new TrustManager[] {
				new X509TrustManager() {
					public java.security.cert.X509Certificate[] getAcceptedIssuers() {
						return null;
					}
					public void checkClientTrusted(X509Certificate[] certs, String authType){}
					public void checkServerTrusted(X509Certificate[] certs, String authType){}
				}
			};

			// Install the all-trusting trust manager
			SSLContext sc = SSLContext.getInstance("SSL");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

			// Create all-trusting host name verifier
			HostnameVerifier allHostsValid = new HostnameVerifier() {
				public boolean verify(String hostname, SSLSession session){
					return true;
				}
			};

			// Install the all-trusting host verifier
			HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

java에서 실행 방법

public class Downloadtest {
	public View getFileDownload() {
        String fullPath="https://test.test.com/test.zip";
        String fileNm = "change_test.zip";
        return new FileUrlDownloadResponse(fullPath, fileNm);
    }
}

 

사용 방식은 일부로 비슷하게 만들었다.

 

특이사항으로는 2번 방식(url) 에 ssl 인증을 통과 시키는 소스가 있다.

 

원래는 이 부분은 없어도 되는데, 현재 회사에서 개발 서버의 cdn이 잘못된 인증서로 되어있어서,

https:// 이지만 실제로는 없는 인증서의 url로 첨부파일이 올라간다.

 

운영에서는 괜찮겠지만, 개발 단계에서는 ssl 오류때문에, 이 문제를 피할려고 disableSslVerification() 소스를 추가하였다.

복잡해 보이지만, 그냥 단순하게 말하자면 ssl 인증을 피하는 소스이다.

한마디로 https로 호출은 하지만 실제로는 ssl 적용이 안된 url 호출을 위한 사용이라고 보면 된다.

 

이만 마치겠다. 고생해라



이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공유하기 링크
Comments