본문 바로가기
프론트공부

[JS] 자바스크립트로 다중 Sheet 엑셀 다운로드 / Export Multi sheets Excel Using JavaScript

by 으노으뇨 2022. 11. 21.
728x90
반응형
SMALL

안녕하세요~!!! 이번에는 자바스크립트로 다중 Sheet 엑셀을 다운로드 하는 방법에 대해서 알아보겠습니다!!

이번에도 인터넷에 주로 있는 방법을 제가 조금 소스를 수정해서 작성해보았습니다!!

우선 지난 포스팅에는 인터넷에 주로 흔하게 떠도는 

자바스크립트로 엑셀 파일 추출하기

주제로 작성했었습니다. https://uno-kim.tistory.com/228

 

[JS]자바스크립트를 이용해서 웹페이지의표를 엑셀로 다운로드하기 / js 엑셀 다운로드 / 라이브

안녕하세요~! ㅎㅎ 오늘은 JavaScript를 이용해서 웹단의 표를 엑셀로 바로 다운로드 받는 소스를 기록하고자합니다. 우선 다른 포스팅을 봤는데 다들 너무 잘 작성해서 좋았지만 그래도 제 방식대

uno-kim.tistory.com

만약 다중 시트가 아닌 하나의 시트만있는 엑셀파일을 가져오는 경우에 대해서는 윗 포스팅을 이용해주세요!!!


우선 상황입니다.

1. 사업전략팀에서 각 사업별로 엑셀을 다운로드 받을 수 있어야한다.
2. 해당 엑셀은 시트별로 1,2,3,4 각 분기별로 테이블이 저장되어야한다.

1. JSP 예제 소스

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<head>
<meta name="Author" content="serpiko@hanmail.net" />
<meta name="description" content="http://serpiko.tistory.com" />

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<!--<meta name="viewport" content="width=device-width, initial-scale=1">-->
<meta name="format-detection" content="telephone=no" />
<title>Document</title>
<style>
table, table td {
	border: 1px solid #000;
}
</style>
</head>
<body>
	<table class="" border="0" cellspacing="0" id='tbl1'>
		<colgroup>
			<col width="3%">
			<col width="130">
			<col width="130">
			<col width="130">
		</colgroup>
		<thead>
			<tr>
				<th rowspan="2">no</th>
				<th rowspan="2">title</th>
				<th rowspan="2">count1</th>
				<th rowspan="2">count2</th>
				<th rowspan="2">total amount</th>
				<th rowspan="2">percent</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">1</button>
				</td>
				<td>book</td>
				<td data-type="Number" data-style='Number'>5,944,728</td>
				<td data-type="Number">32</td>
				<td data-type="Number">5,944,760</td>
				<td data-type="Number">0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">2</button>
				</td>
				<td>coffee</td>
				<td>5,918,064</td>
				<td>45</td>
				<td>5,918,109</td>
				<td>0.99</td>
			</tr>
		</tbody>
	</table>
	<hr>
	<table class="" border="0" cellspacing="0" id='tbl2'>
		<colgroup>
			<col width="3%">
			<col width="130">
			<col width="130">
			<col width="130">
		</colgroup>
		<thead>
			<tr>
				<th rowspan="2">no</th>
				<th rowspan="2">국가명</th>
				<th rowspan="2">number</th>
				<th rowspan="2">percent</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">1</button>
				</td>
				<td>오스트레일리아</td>
				<td>686,283</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">2</button>
				</td>
				<td>과들루프</td>
				<td>388,991</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">3</button>
				</td>
				<td>탄자니아</td>
				<td>378,396</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">4</button>
				</td>
				<td>기니</td>
				<td>378,393</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">5</button>
				</td>
				<td>소말리아</td>
				<td>377,008</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">6</button>
				</td>
				<td>니제르</td>
				<td>375,502</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">7</button>
				</td>
				<td>아프가니스탄</td>
				<td>375,193</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">8</button>
				</td>
				<td>볼리비아</td>
				<td>370,804</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">9</button>
				</td>
				<td>쿡 제도</td>
				<td>368,392</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">10</button>
				</td>
				<td>토켈라우 토켈라우</td>
				<td>368,197</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">11</button>
				</td>
				<td>피지</td>
				<td>366,884</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">12</button>
				</td>
				<td>중화인민공화국</td>
				<td>365,197</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">13</button>
				</td>
				<td>생피에르 미클롱</td>
				<td>364,256</td>
				<td>0.99</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">14</button>
				</td>
				<td>체코</td>
				<td>363,200</td>
				<td>0.99</td>
			</tr>
		</tbody>
	</table>
	<hr />
	<table class="" border="0" cellspacing="0" id='tbl3'>
		<colgroup>
			<col width="10%">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
			<col width="">
		</colgroup>
		<thead>
			<tr>
				<th rowspan="2">국가명</th>
				<th rowspan="2">A</th>
				<th rowspan="2">B</th>
				<th rowspan="2">C</th>
				<th rowspan="2">D</th>
				<th rowspan="2">E</th>
				<th rowspan="2">F</th>
				<th rowspan="2">G</th>
				<th rowspan="2">H</th>
				<th rowspan="2">I</th>
				<th rowspan="2">J</th>
				<th rowspan="2">K</th>
				<th rowspan="2">L</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">가나</button>
				</td>
				<td>25,800 (0.99 %)</td>
				<td>32,399 (0.99 %)</td>
				<td>31,799 (0.99 %)</td>
				<td>29,599 (0.99 %)</td>
				<td>32,364 (0.99 %)</td>
				<td>26,999 (0.99 %)</td>
				<td>28,366 (0.99 %)</td>
				<td>32,200 (0.99 %)</td>
				<td>28,599 (0.99 %)</td>
				<td>32,200 (0.99%)</td>
				<td>29,364 (0.99%)</td>
				<td>33,400 (0.99%)</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">가봉</button>
				</td>
				<td>27,599 (0.99 %)</td>
				<td>30,364 (0.99 %)</td>
				<td>30,165 (0.99 %)</td>
				<td>30,164 (0.99 %)</td>
				<td>24,766 (0.99 %)</td>
				<td>29,199 (0.99 %)</td>
				<td>28,196 (0.99 %)</td>
				<td>26,998 (0.99 %)</td>
				<td>33,000 (0.99 %)</td>
				<td>27,600 (0.99%)</td>
				<td>28,999 (0.99%)</td>
				<td>29,200 (0.99%)</td>
			</tr>
			<tr>
				<td>
					<button class="btn_txt_green" type="button">가이아나</button>
				</td>
				<td>31,200 (0.99 %)</td>
				<td>30,166 (0.99 %)</td>
				<td>28,400 (0.99 %)</td>
				<td>27,198 (0.99 %)</td>
				<td>27,566 (0.99 %)</td>
				<td>30,599 (0.99 %)</td>
				<td>27,400 (0.99 %)</td>
				<td>31,166 (0.99 %)</td>
				<td>28,800 (0.99 %)</td>
				<td>31,998 (0.99%)</td>
				<td>28,998 (0.99%)</td>
				<td>28,598 (0.99%)</td>
			</tr>
		</tbody>
	</table>
	<button id='btn'>Export to Excel</button>
	<script src="testJS.js"></script>
</body>
</html>

각 테이블 별 아이디를 주었습니다.

각 테이블마다 tbl1, tbl2, tbl3 

만약 나중에 응용하게 된다면 해당 테이블들을 우선 안보이게라도 웹상에서 hidden 처리해서 

나오게끔 해주시거나 하면됩니다.


2. JavaScript 소스

window.addEventListener('load', function(){
	document.getElementById('btn').addEventListener('click',function(){
		_tablesToExcel(['tbl1','tbl2', 'tbl3'], ['시트명1','시트명2', '시트명3'], '통계.xls');
	})
})
var tmplWorkbookXML = `
	<?xml version="1.0"?>
		<?mso-application progid="Excel.Sheet"?>
			<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
				<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
					<Author>serpiko</Author>
					<Created>{created}</Created>
				</DocumentProperties>
				<Styles>
					<Style ss:ID="Currency">
						<NumberFormat ss:Format="Currency">
						</NumberFormat>
					</Style>
					<Style ss:ID="Date">
						<NumberFormat ss:Format="Medium Date">
						</NumberFormat>
					</Style>
					<Style ss:ID="Number">
						<NumberFormat ss:Format="#,##0_ ">
						</NumberFormat>
					</Style>
					</Styles>
					{worksheets}
				</Workbook>`;
var tmplWorksheetXML = `
				<Worksheet ss:Name="{nameWS}">
					<Table ss:DefaultColumnWidth="100">
						{rows}
					</Table>
				</Worksheet>`;
var tmplCellXML = `
				<Cell{attributeStyleID}{attributeFormula}>
					<Data ss:Type="{nameType}">{data}</Data>
				</Cell>`;
function base64(sheetXML) {
	return window.btoa(unescape(encodeURIComponent(sheetXML)))
}
function format(s, c) {
	return s.replace(/{(\w+)}/g, function(m, p) {
		return c[p];
	})
}

function _tablesToExcel(tables, wsnames, wbname) {
	let ctx = "";
	let workbookXML = "";
	let worksheetsXML = "";
	let rowsXML = "";
	for (let i = 0; i < tables.length; i++) {
		if (!tables[i].nodeType) 
		{
			tables[i] = document.getElementById(tables[i]);
		}
		for (let j = 0; j < tables[i].rows.length; j++) 
		{
			rowsXML += '<Row>'
			for (let k = 0; k < tables[i].rows[j].cells.length; k++) 
			{
				let dataType = tables[i].rows[j].cells[k].getAttribute("data-type");
				let dataStyle = tables[i].rows[j].cells[k].getAttribute("data-style");
				let dataValue = tables[i].rows[j].cells[k].getAttribute("<em></em>data-value");
				dataValue = (dataValue) ? dataValue
						: tables[i].rows[j].cells[k].innerHTML;
				let dataFormula = tables[i].rows[j].cells[k].getAttribute("data-formula");
				dataFormula = (dataFormula) ? dataFormula 
						: (dataType == 'DateTime') ? dataValue
								: null;
				ctx = {
					attributeStyleID : (dataStyle == 'Currency'
							|| dataStyle == 'Date' || dataStyle == 'Number') ? ' ss:StyleID="'
							+ dataStyle + '"'
							: '',
					nameType : (dataType == 'Number'
							|| dataType == 'DateTime'
							|| dataType == 'Boolean' || dataType == 'Error') ? dataType
							: 'String',
					data : (dataFormula) ? '' : dataValue,
					attributeFormula : (dataFormula) ? ' ss:Formula="'
							+ dataFormula + '"'
							: ''
				};
				rowsXML += format(tmplCellXML, ctx);
			}
			rowsXML += '</Row>'
		}
		ctx = {
			rows : rowsXML,
			nameWS : wsnames[i] || 'Sheet' + i
		};
		worksheetsXML += format(tmplWorksheetXML, ctx);
		rowsXML = "";
	}
	ctx = {
		created : (new Date()).getTime(),
		worksheets : worksheetsXML
	};
	workbookXML = format(tmplWorkbookXML, ctx);

	let link = document.createElement("a");
	link.href = 'data:application/vnd.ms-excel;base64,' + base64(workbookXML);
	link.download = wbname;
	document.body.appendChild(link);
	link.click();
	document.body.removeChild(link);
};

유투브 출처 : https://www.youtube.com/watch?v=Wm1JBxhpyro 

국내 포스팅 출처 : https://serpiko.tistory.com/844#google_vignette

우선 해당 위의 포스팅과 영상을 참고했습니다.

그러나... 너무 실력드링 좋아서 그런지 저 처럼 코딩을 처음 시작한 사람에겐 보기가 너무 어려웠습니다.

그래서 초보자들이 보기 편하게 조금 수정해보았습니다.

변경점

  • 버튼엘리멘트의 onclick 방식을 id에 이벤트를 JS단에서 명시해주었다.
  • 함수호출과 동시 변수들을 선언했다면 그것을 js 전역변수로 사용할 수 있게 전역변수화 시킴
  • 불필요한 매개변수를 하나를 삭제하고 본문에서도 필요없는 소스 삭제
  • format이라던가 base64의 경우 조금더 직관적으로 함수로 빼서 사용
  • 싱글커텐션으로 되어있는 1자형 전역변수들을 `` 를 사용하여 입체적으로 한눈에 어떤건지 볼 수 있게 하였음

 

나중에 위의 상황과 같은 업무 또는 과제를 받았을 때 바로 참고해서 사용할 수 있게 메모형식으로 작성했으며 

해당 기능은

엑셀다운로드 기능하는 함수에 테이블ID 를 리스트형태로 보내고,

또 해당 테이블 3개에 대한 각 시트이름을 보내고,

파일이름도 매개변수로 보내고 있습니다.

따라서 실제 실행한다면 위와 같은 결과를 얻을 수 있습니다.

이것으로 다중 Sheet 엑셀파일로 다운로드 하는 방법에 대해서 알아보았습니다.

이 방법은

엑셀 파일 3개를 각 파일명을 시트명으로 하고 그것을 하나의 파일로 생성하여 만드는 것처럼 생각하면 어떤 파일이 만들어져야할지 가닥이 잡혀서 쉽게 이해 할 수 있을 것같습니다.

긴글 읽어 주셔서 감사합니다.

728x90
반응형
LIST

댓글