DownloadView, XLSView, XLSWriter 제거
parent
367d20436a
commit
35af3fd4ad
@ -1,183 +0,0 @@
|
|||||||
package cokr.xit.base.file.web;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.springframework.web.servlet.view.AbstractView;
|
|
||||||
|
|
||||||
import cokr.xit.base.file.FileInfo;
|
|
||||||
import cokr.xit.foundation.Assert;
|
|
||||||
import cokr.xit.foundation.Downloadable;
|
|
||||||
|
|
||||||
/**파일의 다운로드를 처리하기 위한 {@link org.springframework.web.servlet.View}
|
|
||||||
* <p>DownloadView가 파일을 다운로드하기 위해서는 {@link org.springframework.web.servlet.ModelAndView}로 다음과 같은 속성의 값을 지정해야 한다.
|
|
||||||
* <ul><li>"file" - {@link File}, {@link InputStream}, 또는 {@link FileInfo}</li>
|
|
||||||
* <li>"filename" - 다운로드될 때 사용자가 보게될 파일이름. "file"이 InputStream일 경우는 필수</li>
|
|
||||||
* <li>"contentType" - 컨텐트 유형. 지정하지 않으면 "file" 속성의 객체를 검사하여 추측하며, 알아낼 수 없으면 'application/octet-stream'을 설정
|
|
||||||
* <li>"disposition" - 응답의 Content-Disposition 헤더값. 지정하지 않으면 'attachment'를 설정</li>
|
|
||||||
* <li>"length" - 파일 길이. 지정하지 않으면 "file" 속성의 객체를 검사하여 설정한다. "file" 속성 객체가 InputStream일 경우는 필수</li>
|
|
||||||
* <li>"delete" - "file" 속성의 객체가 {@link File}일 경우 삭제 여부(true 또는 false). 지정하지 않으면 다운로드 후 삭제한다.</li>
|
|
||||||
* </ul>
|
|
||||||
* @author mjkhan
|
|
||||||
*/
|
|
||||||
public class DownloadView extends AbstractView {
|
|
||||||
@Override
|
|
||||||
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest hreq, HttpServletResponse hresp) throws Exception {
|
|
||||||
if (model.containsKey("download")) {
|
|
||||||
render((Downloadable)model.get("download"), hresp);
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
InputStream input = getInputStream(model);
|
|
||||||
if (input == null) {
|
|
||||||
hresp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
|
||||||
hresp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (InputStream stream = input) {
|
|
||||||
String filename = getFilename(model);
|
|
||||||
if (Assert.isEmpty(filename))
|
|
||||||
throw new IllegalArgumentException("Unable to determine the filename");
|
|
||||||
|
|
||||||
String contentType = getContentType(model);
|
|
||||||
if (Assert.isEmpty(contentType)) {
|
|
||||||
contentType = Assert.ifEmpty(URLConnection.guessContentTypeFromName(filename), "application/octet-stream");
|
|
||||||
}
|
|
||||||
String disposition = Assert.ifEmpty((String)model.get("disposition"), "attachment");
|
|
||||||
|
|
||||||
String charset = getCharset(model);
|
|
||||||
hresp.setCharacterEncoding(charset);
|
|
||||||
hresp.setContentType(contentType);
|
|
||||||
hresp.setHeader("Content-Disposition", disposition + "; filename=\"" + URLEncoder.encode(filename, charset) +"\"");
|
|
||||||
Number length = getLength(model);
|
|
||||||
if (length != null)
|
|
||||||
hresp.setContentLengthLong(length.longValue());
|
|
||||||
|
|
||||||
stream.transferTo(hresp.getOutputStream());
|
|
||||||
} finally {
|
|
||||||
clear(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getCharset(Map<String, Object> model) {
|
|
||||||
Object obj = model.get("charset");
|
|
||||||
return obj != null ? obj.toString() : "UTF-8";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream getInputStream(Map<String, Object> model) throws Exception {
|
|
||||||
Object obj = model.get("file");
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
if (obj instanceof InputStream)
|
|
||||||
return (InputStream)obj;
|
|
||||||
if (obj instanceof File) {
|
|
||||||
File file = (File)obj;
|
|
||||||
return new FileInputStream(file);
|
|
||||||
}
|
|
||||||
if (obj instanceof FileInfo) {
|
|
||||||
FileInfo fileInfo = (FileInfo)obj;
|
|
||||||
return fileInfo.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("'file' must be either a " + File.class.getName() + ", a " + InputStream.class.getName() + ", or a " + FileInfo.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFilename(Map<String, Object> model) {
|
|
||||||
String filename = (String)model.get("filename");
|
|
||||||
if (!Assert.isEmpty(filename)) return filename;
|
|
||||||
|
|
||||||
Object obj = model.get("file");
|
|
||||||
if (obj instanceof File) {
|
|
||||||
File file = (File)obj;
|
|
||||||
return file.getName();
|
|
||||||
}
|
|
||||||
if (obj instanceof FileInfo) {
|
|
||||||
FileInfo fileInfo = (FileInfo)obj;
|
|
||||||
return fileInfo.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = model.get("download");
|
|
||||||
if (obj instanceof Downloadable) {
|
|
||||||
Downloadable downloadable = (Downloadable)obj;
|
|
||||||
return downloadable.getFilename();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getContentType(Map<String, Object> model) throws Exception {
|
|
||||||
String contentType = (String)model.get("contentType");
|
|
||||||
if (!Assert.isEmpty(contentType)) return contentType;
|
|
||||||
|
|
||||||
Object obj = model.get("file");
|
|
||||||
if (obj instanceof InputStream) {
|
|
||||||
InputStream input = (InputStream)obj;
|
|
||||||
return URLConnection.guessContentTypeFromStream(input);
|
|
||||||
}
|
|
||||||
if (obj instanceof File) {
|
|
||||||
File file = (File)obj;
|
|
||||||
return URLConnection.guessContentTypeFromName(file.getName());
|
|
||||||
}
|
|
||||||
if (obj instanceof FileInfo) {
|
|
||||||
FileInfo fileInfo = (FileInfo)obj;
|
|
||||||
return fileInfo.getMimeType();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Number getLength(Map<String, Object> model) {
|
|
||||||
Number length = (Number)model.get("length");
|
|
||||||
if (!Assert.isEmpty(length)) return length;
|
|
||||||
|
|
||||||
Object obj = model.get("file");
|
|
||||||
if (obj instanceof File) {
|
|
||||||
File file = (File)obj;
|
|
||||||
return file.length();
|
|
||||||
}
|
|
||||||
if (obj instanceof FileInfo) {
|
|
||||||
FileInfo fileInfo = (FileInfo)obj;
|
|
||||||
return fileInfo.getSize();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void clear(Map<String, Object> model) {
|
|
||||||
Object obj = model.get("file");
|
|
||||||
if (!(obj instanceof File)) return;
|
|
||||||
|
|
||||||
Boolean delete = (Boolean)model.get("delete");
|
|
||||||
if (Boolean.FALSE.equals(delete)) return;
|
|
||||||
|
|
||||||
File file = (File)obj;
|
|
||||||
if (file.exists())
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void render(Downloadable downloadable, HttpServletResponse hresp) throws Exception {
|
|
||||||
if (downloadable == null) {
|
|
||||||
hresp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
|
||||||
hresp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String filename = downloadable.getFilename();
|
|
||||||
if (Assert.isEmpty(filename))
|
|
||||||
throw new IllegalArgumentException("Unable to determine the filename");
|
|
||||||
|
|
||||||
String charset = downloadable.getCharset();
|
|
||||||
hresp.setCharacterEncoding(charset);
|
|
||||||
hresp.setContentType(downloadable.getContentType());
|
|
||||||
hresp.setHeader("Content-Disposition", downloadable.getDisposition() + "; filename=\"" + URLEncoder.encode(filename, charset) +"\"");
|
|
||||||
Long length = downloadable.getLength();
|
|
||||||
if (length != null)
|
|
||||||
hresp.setContentLengthLong(length.longValue());
|
|
||||||
|
|
||||||
downloadable.write(hresp.getOutputStream());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package cokr.xit.base.file.web;
|
|
||||||
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.springframework.web.servlet.view.AbstractView;
|
|
||||||
|
|
||||||
import cokr.xit.base.file.xls.XLS;
|
|
||||||
|
|
||||||
/**사용자가 요청하는 데이터를 엑셀파일로 제공하는 뷰.
|
|
||||||
* 사용 순서는 다음과 같다.
|
|
||||||
* 엑셀 템플릿파일 작성. 템플릿 작성법은
|
|
||||||
* https://jxls.sourceforge.net
|
|
||||||
* https://ddoriya.tistory.com/entry/JXLS-POI-JAVA%EC%97%90%EC%84%9C-Excel-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EC%A2%85%EB%A5%98-%EB%B9%84%EA%B5%90
|
|
||||||
* 스프링 파일에 'xlsView' Bean 설정
|
|
||||||
* 콘트롤러(*Controller.java)
|
|
||||||
* ModelAndView의 이름을 'xlsView'로 지정
|
|
||||||
* ModelAndView에
|
|
||||||
* 엑셀 처리기 ('xls') 설정
|
|
||||||
* DatasetControl (JSP / *.js)
|
|
||||||
* 파라미터 설정
|
|
||||||
* download() 호출
|
|
||||||
*
|
|
||||||
* @author mjkhan
|
|
||||||
*/
|
|
||||||
public class XLSView extends AbstractView {
|
|
||||||
@Override
|
|
||||||
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest hreq, HttpServletResponse hresp) throws Exception {
|
|
||||||
XLS xls = (XLS)model.get("xls");
|
|
||||||
|
|
||||||
String
|
|
||||||
charset = "UTF-8",
|
|
||||||
filename = xls.getFilename();
|
|
||||||
|
|
||||||
hresp.setCharacterEncoding(charset);
|
|
||||||
hresp.setContentType(XLS.MIME_TYPE);
|
|
||||||
hresp.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(filename, charset) +"\"");
|
|
||||||
|
|
||||||
xls.write(hresp.getOutputStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFilename(Map<String, Object> model) {
|
|
||||||
XLS xls = (XLS)model.get("xls");
|
|
||||||
return xls != null ? xls.getFilename() : null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package cokr.xit.base.file.xls;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jxls.common.Context;
|
|
||||||
import org.jxls.util.JxlsHelper;
|
|
||||||
|
|
||||||
public class JXLS extends XLS {
|
|
||||||
private Context ctx;
|
|
||||||
private Map<String, Object> data;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JXLS setTemplate(String template) {
|
|
||||||
super.setTemplate(template);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JXLS setFilename(String filename) {
|
|
||||||
super.setFilename(filename);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**엑셀 파일에 저장할 데이터들을 반환한다.
|
|
||||||
* @return 엑셀 파일에 저장할 데이터
|
|
||||||
*/
|
|
||||||
public Map<String, Object> getData() {
|
|
||||||
return data != null ? data : Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**엑셀 파일에 저장할 데이터들을 설정한다.
|
|
||||||
* @param map 엑셀 파일에 저장할 데이터
|
|
||||||
* @return 현재 XLS
|
|
||||||
*/
|
|
||||||
public JXLS setData(Map<String, Object> map) {
|
|
||||||
data = map;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**엑셀 파일에 저장할 데이터들을 설정한다.
|
|
||||||
* @param key 데이터 키
|
|
||||||
* @param obj 데이터
|
|
||||||
* @return 현재 XLS
|
|
||||||
*/
|
|
||||||
public JXLS setData(String key, Object obj) {
|
|
||||||
if (data == null) {
|
|
||||||
data = new LinkedHashMap<>();
|
|
||||||
}
|
|
||||||
data.put(key, obj);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(OutputStream out) {
|
|
||||||
ctx = new Context();
|
|
||||||
getData().forEach(ctx::putVar);
|
|
||||||
|
|
||||||
try (InputStream input = loadTemplate();) {
|
|
||||||
JxlsHelper.getInstance().processTemplate(input, out, ctx);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw runtimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
package cokr.xit.base.file.xls;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
|
|
||||||
import cokr.xit.foundation.AbstractComponent;
|
|
||||||
|
|
||||||
/**엑셀 스프레드시트의 읽기나 쓰기를 돕는 유틸리티
|
|
||||||
* @author mjkhan
|
|
||||||
*/
|
|
||||||
public abstract class XLS extends AbstractComponent {
|
|
||||||
/**엑셀 파일(*.xlsx)의 mime type */
|
|
||||||
public static final String MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
||||||
|
|
||||||
private String
|
|
||||||
template,
|
|
||||||
filename;
|
|
||||||
|
|
||||||
/**템플릿 파일경로를 반환한다.
|
|
||||||
* @return 템플릿 파일경로
|
|
||||||
*/
|
|
||||||
public String getTemplate() {
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InputStream loadTemplate() throws Exception {
|
|
||||||
if (isEmpty(template))
|
|
||||||
return null;
|
|
||||||
return new ClassPathResource(template).getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**템플릿 파일경로를 설정한다.
|
|
||||||
* @param template 템플릿 파일경로
|
|
||||||
* @return 현재 XLS
|
|
||||||
*/
|
|
||||||
public XLS setTemplate(String template) {
|
|
||||||
this.template = template;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**저장할 파일이름을 반환한다.
|
|
||||||
* 디폴트는 'yyyyMMdd-HHmmss.xlsx'
|
|
||||||
* @return 저장할 파일이름
|
|
||||||
*/
|
|
||||||
public String getFilename() {
|
|
||||||
return ifEmpty(filename, () -> new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".xlsx");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**저장할 파일이름을 설정한다.
|
|
||||||
* 디폴트는 'yyyyMMdd-HHmmss.xlsx'
|
|
||||||
* @param filename 저장할 파일이름
|
|
||||||
* @return 현재 XLS
|
|
||||||
*/
|
|
||||||
public XLS setFilename(String filename) {
|
|
||||||
this.filename = filename;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**설정된 데이터를 엑셀 파일 포맷으로 out에 저장한다.
|
|
||||||
* @param out OutputStream
|
|
||||||
*/
|
|
||||||
public abstract void write(OutputStream out);
|
|
||||||
|
|
||||||
/**yyyyMMdd, 또는 yyMMdd 포맷의 문자열을 yyyy-MM-dd 포맷으로 변환하여 반환한다.
|
|
||||||
* @param obj yyyyMMdd, 또는 yyMMdd 포맷의 문자열
|
|
||||||
* @return yyyy-MM-dd 포맷 문자열
|
|
||||||
*/
|
|
||||||
public final String str2date(Object obj) {
|
|
||||||
if (isEmpty(obj)) return "";
|
|
||||||
|
|
||||||
String str = obj.toString();
|
|
||||||
int length = str.length(),
|
|
||||||
pos = length - 2;
|
|
||||||
|
|
||||||
String
|
|
||||||
day = str.substring(pos),
|
|
||||||
month = str.substring(pos = pos - 2, pos + 2),
|
|
||||||
year = str.substring(0, pos);
|
|
||||||
return String.format("%s-%s-%s", year, month, day);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**HHmmss 포맷의 문자열을 HH:mm:ss 포맷으로 변환하여 반환한다.
|
|
||||||
* @param obj HHmmss 포맷의 문자열
|
|
||||||
* @return HH:mm:ss 포맷 문자열
|
|
||||||
*/
|
|
||||||
public final String str2time(Object obj) {
|
|
||||||
if (isEmpty(obj)) return "";
|
|
||||||
|
|
||||||
String str = obj.toString();
|
|
||||||
int length = str.length(),
|
|
||||||
pos = length - 2;
|
|
||||||
|
|
||||||
String
|
|
||||||
ss = str.substring(pos),
|
|
||||||
mm = str.substring(pos = pos - 2, pos + 2),
|
|
||||||
hh = str.substring(0, pos);
|
|
||||||
return String.format("%s:%s:%s", hh, mm, ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**yyyyMMddHHmmss, 또는 yyMMddHHmmss 포맷의 문자열을 yyyy-MM-dd HH:mm:ss 포맷으로 변환하여 반환한다.
|
|
||||||
* @param obj yyyyMMddHHmmss, 또는 yyMMddHHmmss 포맷의 문자열
|
|
||||||
* @return yyyy-MM-dd HH:mm:ss 포맷 문자열
|
|
||||||
*/
|
|
||||||
public final String str2datetime(Object obj) {
|
|
||||||
if (isEmpty(obj)) return "";
|
|
||||||
|
|
||||||
String str = obj.toString();
|
|
||||||
int length = str.length(),
|
|
||||||
pos = length - 6;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String
|
|
||||||
date = str.substring(0, pos),
|
|
||||||
time = str.substring(pos);
|
|
||||||
|
|
||||||
return str2date(date) + " " + str2time(time);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue