본문 바로가기

Spring

Geoserver 에 발행된 레이어 다운로드(.zip)

반응형

Openlayers 를 개발할 때, 가장 많이 사용하는 GIS 서버는 역시 Geoserver 이다. Geoserver를 통해 wms, wfs 레이어를 가져올 수 있고, 상황에 따라 tif 파일도 레이어로 발행해서 웹 지도상에 가져올 수 있다. Geoserver는 많은 기능을 제공하는데, 그 중에서 특정 형식의 포맷으로 다운로드 할 수 있는 기능의 샘플 소스를 확인해보려고 한다.

 

package songjang.map;

import egovframework.com.cmm.service.EgovProperties;
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.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Geoserver 파일 다운로드 컨트롤러
 * @author 송장
 * @since 2018.06.19
 * @version 1.0
 * @see
 *
 * <pre>
 * << 개정이력(Modification Information) >>
 *
 *      수정일            수정자                 수정내용
 *  -----------      ---------    ---------------------------
 *   2018.06.19         송장                  최초 생성
 *
 * </pre>
 */
@Controller
public class GeoserverDownloadController {

	private static final Logger LOGGER = LoggerFactory.getLogger(GeoserverDownloadController.class);
	private String GEOSERVER_URL = "http://localhost:8080/geoserver/songjang/wfs";

	/**
	 * 한글 값 처리
	 * @param value 인코딩할 문자열
	 * @return
	 * @throws java.io.UnsupportedEncodingException
	 */
	private String getLocaleString(String value) throws UnsupportedEncodingException {
		byte[] b;
		b = value.getBytes("8859_1");
		final CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
		try {
			final CharBuffer r = decoder.decode(ByteBuffer.wrap(b));
			return r.toString();
		} catch (final CharacterCodingException e) {
			return new String(b, "EUC-KR");
		}
	}

	/**
	 * Export (여러개 일 때 한개(.zip)로 압축)
	 *
	 * @param params
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value="/map/dxfExport.do")
	public void dxfExport(@RequestParam HashMap<String, Object> params, HttpServletRequest request, HttpServletResponse response) throws Exception {
		String[] arrLayer = ((String)params.get("layerNames")).split("/");

		String fileName = arrLayer.length == 1 ? arrLayer[0] + ".dxf" : "dxf.zip";

		response.setCharacterEncoding("UTF-8");
		response.setHeader("Accept-Ranges", "bytes");
		String agent = request.getHeader("User-Agent");
		fileName = URLEncoder.encode(fileName, "UTF-8");

		if(agent.indexOf("MSIE 5.5") > -1){
			response.setHeader("Content-Disposition", "filename=" + fileName + ";");
		} else{
			response.setHeader("Content-Type", "application/octet-stream;charset=utf-8;");
			response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ";");
		}

		request.setCharacterEncoding("UTF-8");
		StringBuffer sb = new StringBuffer();
		for(Object param : request.getParameterMap().entrySet()) {
			@SuppressWarnings("unchecked")
			Entry<String, String[]> entry = (Entry<String, String[]>) param;

			if ("layerNames".equals(entry.getKey())) continue;

			if(entry.getKey().indexOf('=') >= 0) {
				sb.append(getLocaleString(entry.getKey()));
			} else {
				sb.append(entry.getKey());
				sb.append("=");

				String[] values = entry.getValue();
				if(values.length > 0) {
					String layerName = request.getCharacterEncoding() == null ? URLEncoder.encode(getLocaleString(values[0]), "UTF-8") : URLEncoder.encode(values[0], "UTF-8");
					sb.append(layerName);
				}
				sb.append("&");
			}
		}

		HttpURLConnection huc = null;
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		ZipOutputStream zout = null;

		try {
			if (arrLayer.length == 1) {
				URL url = new URL(GEOSERVER_URL.concat("?") + sb + "typeName=" + arrLayer[0]);

				URLConnection connection = url.openConnection();

				huc = (HttpURLConnection)connection;
				huc.setRequestMethod("GET");
				huc.setDoOutput(true);
				huc.setDoInput(true);
				huc.setUseCaches(false);
				huc.setDefaultUseCaches(false);
				huc.setConnectTimeout(1000000);
				huc.setReadTimeout(1000000);

				bis = new BufferedInputStream(huc.getInputStream());

				bos = new BufferedOutputStream(response.getOutputStream());

				byte[] inputData = null;

				int read = 0;
				int bufSize = 4096;
				inputData = new byte[bufSize];
				while ((read = bis.read(inputData)) > 0) {
					bos.write(inputData, 0, read);
				}

				bos.flush();

			} else if (arrLayer.length > 1) {

				zout = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));

				for (String layerName : arrLayer) {
					URL url = new URL(GEOSERVER_URL.concat("?") + sb + "typeName=" + layerName);

					URLConnection connection = url.openConnection();

					huc = (HttpURLConnection)connection;
					huc.setConnectTimeout(1000000);
					huc.setReadTimeout(500000);

					bis = new BufferedInputStream(huc.getInputStream());

					ZipEntry ze = new ZipEntry(layerName + ".dxf");
					zout.putNextEntry(ze);

					int read = 0;
					int bufSize = 4096;
					byte[] inputData = new byte[bufSize];
					while ((read = bis.read(inputData)) > 0) {
						zout.write(inputData, 0, read);
					}

					zout.closeEntry();
//					zout.flush();
					bis.close();
				}
			} else {
				LOGGER.debug("retry!!");
			}
		} catch (IOException e) {
			LOGGER.warn(e.getMessage());
			//throw e;
//			e.printStackTrace();
		} finally {
			if(bis != null) {
				bis.close();
			}
			if(bos != null) {
				bos.close();
			}
			if(huc != null) {
				huc.disconnect();
			}
			if(zout != null) {
				zout.close();
			}
		}
	}

}

원리는 간단했다. Geoserver에서 파일 export url을 지원하기 때문에 내 컨트롤러(Java) 에서 URLConnection 을 통해 지오서버로 요청한 방식이다. 이 때, 내가 요청한 레이어 개수가 1개면 파일로, 1개 이상이면 java.util.zip 라이브러리를 사용하여 zip 파일로 묶어서 내려받도록 작성하였다. 

 

이 때, 중요한 것은 현재 소스코드는 dxf 형식을 export 하는 것 위주로 작업하였지만, 상황에 따라 더 유연하게 소스코드를 변경할 수 있는 여지가 있다는 점이다. 

API 를 요청할 때 파라미터 outputFormat 을 통해 Geoserver 에서 내보낼 파일 형식 지정이 가능(Geoserver에서 Export 형식을 지원한다면..) 하고, bbox 를 통해 해당 영역만 추출해서 다운로드 할 수 있다.

 

Front의 요청 부분을 일부 확인해보면 

/**
 * dxf export 함수
 *
 * @param layerNames
 * @param params
 */
const dxfExport = function(layerNames, params) {
	params = $.extend({}, {
		request : "GetFeature",		// 기본
		outputFormat : "dxf",		// 다운로드 받을 확장자
		layerNames : layerNames,	// 요청 레이어 이름 목록(1개 이상)
		bbox: [0, 0, 0, 0]		// 다운로드 추출 영역 (minx, miny, maxx, maxy)
	}, params);

	var $form = $("<form/>");

	$.each(params, function(key, val){
		var inputHtml = "<input type='hidden' id='" + key + "' name='" + key + "' value='" + val + "'/>";
		$form.append(inputHtml);
	});

	$form.appendTo("body");
	$form.attr({ action : contextPath + "map/dxfExport.do", method : "post" }).submit().remove();
};

 

outputFormat 을 유동적으로 변경하여 Geoserver에서 지원하는 export format 을 유연하게 다운로드 할 수 있을 것으로 보인다.

반응형

'Spring' 카테고리의 다른 글

Kabeja 를 통한 DXF 파일 GML 로 parsing 예제  (3) 2021.06.05