PHP & JavaScript 엑셀 파일 다운로드 및 업로드 구현 (PhpSpreadsheet 사용)

PHP 환경에서 엑셀 파일을 다뤄야 할 때, 보통 PHPExcel을 많이 사용하는데 이는 이미 서비스가 2019년에 종료되었으며 공식 문서도 PhpSpreadsheet를 권고한다고 명시되어있다. (현재 PhpSpreadsheet는 PHP 8 이상부터 지원되므로 참고)

PhpSpreadsheet를 사용한 간단한 목록의 엑셀 파일 다운로드 및 업로드(후 데이터 수정) 기능을 구현하는 코드에 대하여 설명하겠다.


1. PhpSpreadsheet 설치

웹 서버에 PhpSpreadsheet를 사용하는 데 필요한 PHP 확장 모듈들이 설치되어 있는지 php-m으로 확인하고 설치한다. 확장 모듈은 xml, gd, mbstring, zip이 필요하다. 설치가 완료되었다면 php-m으로 확장 모듈들의 설치 여부를 한 번 더 확인한다.

php-m
sudo dnf install php-xml php-gd php-mbstring
sudo dnf install php8.2-devel php-pear libzip libzip-devel
sudo pecl install zip
echo "extension=zip.so" | sudo tee /etc/php.d/20-zip.ini

PhpSpreadsheet를 설치하는 데 필요한 PHP 의존성 관리 도구 Composer를 설치한다. 설치가 완료되었다면 composer-V로 Composer의 설치 여부를 확인한다.

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e21205b207c3ff031906575712edab6f13eb0b361f2085f1f1237b7126d785e826a450292b6cfd1d64d92e6563bbde02') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/local/bin/composer
composer-V

PHP 확장 모듈 및 Composer 설치가 완료되었다면 PhpSpreadsheet를 사용할 디렉터리에 PhpSpreadsheet를 설치한다.

composer require phpoffice/phpspreadsheet

2. jQuery 스크립트 태그 추가

파일 최상단 또는 Head 파일에 다음과 같이 작성하여 jQuery를 사용하기 위해 추가한다.

<script src="https://code.jquery.com/jquery-3.6.1.js" integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI=" crossorigin="anonymous"></script>

3. 목록 및 엑셀 다운로드, 업로드 폼 작성

설명을 위해 목록은 페이징 없이 간단하게 작성했다. 목록들을 체크 박스로 선택한 후 엑셀 다운로드 버튼을 클릭하면 해당 목록들이 포함된 엑셀 파일 다운로드가 가능하도록 구현한다.

엑셀 업로드 버튼을 클릭하면 업로드 폼을 띄우고 다운로드 받았던 엑셀 파일의 기타 정보를 입력한 후 엑셀 파일을 업로드하여 폼의 등록 버튼을 클릭하면 입력한 데이터로 기타 정보가 수정되도록 구현한다.

<table>
<tr>
<td>
<button type="button" id="excel_download">엑셀 다운로드</button>
<button type="button" id="excel_upload">엑셀 업로드</button>
</td>
</tr>
</table>
<form>
<table>
<thead>
<tr>
<th><input type= "checkbox" name="chkall" id="chkall" onclick="check_all(this.form)"></th>
<th>번호</th>
<th>가입 일자</th>
<th>아이디</th>
<th>이름</th>
<th>생년월일</th>
<th>전화번호</th>
<th>기타 정보</th>
</tr>
</thead>
<?php
global $mysqli;
$sql = "select * from table order by key desc";
$result = $mysqli->query($sql);
while($row = $result->fetch_array()){
if(empty($row["기타 정보"])){
$row["기타 정보"] = "없음";
}else{
if(mb_strlen($row["기타 정보"], "utf-8") > 15){
$row["기타 정보"] = mb_substr($row["기타 정보"], 0, 15, "utf-8")."...";
}
}
?>
<tr>
<td><input type= "checkbox" name="chk[]" value="<?=$row["key"]?>"></td>
<td><?=$row["key"]?></td>
<td><?=$row["가입 일자"]?></td>
<td><?=$row["아이디"]?></td>
<td><?=$row["이름"]?></td>
<td><?=$row["생년월일"]?></td>
<td><?=$row["전화번호"]?></td>
<td><?=$row["기타 정보"]?></td>
</tr>
<?php
}
?>
</tbale>
</form>
<div id="excel_upload_frm">
<h3>엑셀 업로드</h3>
<span style="color:red;">※ 엑셀 다운로드 후 비어있는 기타 정보를 채우고 업로드하시면 자동으로 등록됩니다.</span>
<form action="엑셀 업로드 처리 파일 경로" id="excel_uploading" enctype="multipart/form-data" method="POST" style="display:none; z-index: 999; text-align:center;">
<input name="target_excel" type="file" id="target_excel">
</form>
<button type="button" id="btn_upload_excel">등록</button>
<button type="button" id="btn_upload_excel_back">닫기</button>
</div>
<script type="text/javascript" src="동작 구현 파일"></script>

4. 동작 구현 파일 작성

체크 박스로 목록들을 선택한 후 엑셀 다운로드를 클릭하면 선택한 목록들의 키값들을 임의의 폼을 생성해 전송하고 선택한 목록이 없으면 예외 처리한다.

엑셀 업로드 버튼을 클릭하면 업로드 폼을 띄우고 엑셀 파일을 업로드 한 후 폼의 등록 버튼을 클릭하여 엑셀 데이터를 전송한다.

function check_all(f){
var chk = document.getElementsByName("chk[]");
for(i=0; i<chk.length; i++) chk[i].checked = f.chkall.checked;
}
$('#excel_download').click(function(){
var chk = document.getElementsByName("chk[]");
var z = 0;
for(i=0; i<chk.length; i++){
if(chk[i].checked){
z++;
}
}
if(z == 0){
alert("다운로드 하실 내역을 선택해주세요.");
return;
}
var chk = document.getElementsByName("chk[]");
var no_arr = [];
for(i=0; i<chk.length; i++){
if(chk[i].checked){
no_arr[no_arr.length] = chk[i].value;
}
}
var excelForm = $("<form></form>");
excelForm.attr("method", "Post");
excelForm.attr("action", "엑셀 다운로드 처리 파일 경로");
excelForm.append($("<input/>", {type: "hidden", name: "noArr", value: no_arr}));
excelForm.appendTo("body");
excelForm.submit();
});
$("#excel_upload").click(function(){
$("#excel_upload_frm").show();
});
$("#btn_upload_excel_back").click(function(){
$("#excel_upload_frm").hide();
});
$("#btn_upload_excel").click(function(){
if($("#target_excel")[0].files.length == 0){
alert("엑셀 파일을 첨부해주세요.");
return false;
}
$("#excel_uploading").submit();
});

체크 박스, 엑셀 업로드 버튼과 폼이 동작하는지 확인한다.


5. 엑셀 다운로드 처리 파일 작성

설치한 PhpSpreadsheet를 사용하기 위해 PhpSpreadsheet를 불러오는 코드를 최상단에 추가한다. 첫 행에는 컬럼명이 출력되도록 설정하고 나머지 열의 크기, 색상, 정렬을 보기 편하게 설정한다. 마지막으로 엑셀 파일 이름에 날짜를 붙여 저장되도록 구현한다.

<?php
require_once($_SERVER["DOCUMENT_ROOT"]."PhpOffice/Psr/autoloader.php 경로");
require_once($_SERVER["DOCUMENT_ROOT"]."PhpOffice/PhpSpreadsheet/autoloader.php 경로");
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
// 첫 행
$spreadsheet->setActiveSheetIndex(0)
->setCellValue("A1", "번호")
->setCellValue("B1", "가입 일자")
->setCellValue("C1", "아이디")
->setCellValue("D1", "이름")
->setCellValue("E1", "생년월일")
->setCellValue("F1", "전화번호")
->setCellValue("G1", "기타 정보");
// 각 열 크기
$spreadsheet->getActiveSheet()->getColumnDimension("A")->setWidth(5);
$spreadsheet->getActiveSheet()->getColumnDimension("B")->setWidth(20);
$spreadsheet->getActiveSheet()->getColumnDimension("C")->setWidth(20);
$spreadsheet->getActiveSheet()->getColumnDimension("D")->setWidth(20);
$spreadsheet->getActiveSheet()->getColumnDimension("E")->setWidth(20);
$spreadsheet->getActiveSheet()->getColumnDimension("F")->setWidth(20);
$spreadsheet->getActiveSheet()->getColumnDimension("G")->setWidth(20);
// 각 열 백그라운드 색상
$spreadsheet->getActiveSheet()->getStyle("A1:G1")->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setRGB("DDDDDD");
// 각 열 정렬
$spreadsheet->getActiveSheet()->getStyle("A1:G1")->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER); // 수평 중앙 정렬
$spreadsheet->getActiveSheet()->getStyle("A:G")->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER); // 수직 중앙 정렬
$spreadsheet->getActiveSheet()->getStyle("A:G")->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
$spreadsheet->getActiveSheet()->getStyle("A:G")->getAlignment()->setWrapText(true); // 셀에 여러 줄 표시
// 각 열 값
$noArr = $_POST["noArr"];
global $mysqli;
$sql = "select * from table where key in (".$noArr.") order by key desc";
$result = $mysqli->query($sql);
$cnt = 2;
while($row = $result->fetch_array()){
$spreadsheet->getActiveSheet()->setCellValue("A".$cnt, $row["key"]);
$spreadsheet->getActiveSheet()->setCellValue("B".$cnt, $row["가입 일자"]);
$spreadsheet->getActiveSheet()->setCellValue("C".$cnt, $row["아이디"]);
$spreadsheet->getActiveSheet()->setCellValue("D".$cnt, $row["이름"]);
$spreadsheet->getActiveSheet()->setCellValue("E".$cnt, $row["생년월일"]);
$spreadsheet->getActiveSheet()->setCellValue("F".$cnt, $row["전화번호"]);
$spreadsheet->getActiveSheet()->setCellValue("G".$cnt, $row["기타 정보"]);
++$cnt;
}
$fileName = "목록".date("Ymd").".xlsx";
$write = new Xlsx($spreadsheet);
ob_end_clean();
header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
header("Content-Disposition: attachment; filename=".$fileName);
header("Cache-Control: max-age=0");
$write->save("php://output");
exit;
?>

엑셀이 잘 다운로드 되는지 확인한다.


6. 엑셀 업로드 처리 파일 작성

다운로드 처리 파일과 같이 PhpSpreadsheet를 불러오는 코드를 최상단에 추가한다. DB의 기타 정보 데이터를 수정하기 위한 함수를 작성하고 발생할 수 있는 에러를 예외 처리한다.

파일을 임시 저장 폴더에 저장하고 PhpSpreadsheet Reader로 저장된 엑셀 파일을 읽는다. 첫 번째 행에는 컬럼명이 출력되어 있으므로 A열 2행부터 데이터를 불러와서 A열(키 값), G열(기타 정보) 데이터를 데이터 수정 함수로 전송한다. 수정 함수가 정상적으로 작동하면 알림 메시지를 띄우고 목록 페이지로 돌아간다.

<?php
require_once($_SERVER["DOCUMENT_ROOT"]."PhpOffice/Psr/autoloader.php 경로");
require_once($_SERVER["DOCUMENT_ROOT"]."PhpOffice/PhpSpreadsheet/autoloader.php 경로");
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
function update_column($data){
global $mysqli;
$sql = "update table set column = '".$data["기본 정보"]."' where key = '".$data["key"]."';";
$result = $mysqli->query($sql);
if($result){
return $sql;
}
}
// 업로드 가능한 확장자 설정
$allowed_file = array('xls', 'xlsx');
$error = $_FILES['target_excel']['error'];
$file_fullname = $_FILES['target_excel']['name'];
$file_name = array_pop(explode('.', $file_fullname));
try {
if($_FILES['target_excel']['error'] != 4){
if($error != UPLOAD_ERR_OK){
switch($error){
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new Exception("파일의 용량이 너무 큽니다. (".$error.")");
break;
case UPLOAD_ERR_NO_FILE:
throw new Exception("파일이 첨부 되지 않았습니다. (".$error.")");
break;
default:
throw new Exception("파일이 제대로 업로드 되지 않았습니다. (".$error.")");
}
exit;
}
if(!in_array($file_name, $allowed_file)){ // 확장자 확인
throw new Exception("업로드가 불가한 파일 형식입니다.");
}
if(!move_uploaded_file($_FILES['target_excel']['tmp_name'], "파일 임시 저장 폴더 경로".$file_fullname)){
throw new Exception("파일 저장에 실패하였습니다.");
}
$file_path = "파일 임시 저장 폴더 경로".$file_fullname;
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load($file_path);
$maxRow = $spreadsheet->getActiveSheet()->getHighestRow(); // 마지막 행
$maxColumn = $spreadsheet->getActiveSheet()->getHighestColumn(); // 마지막 열
$target = "A"."2".":"."$maxColumn"."$maxRow";
$lists = $spreadsheet->getActiveSheet()->rangeToArray($target, NULL, TRUE, FALSE);
foreach($lists as $key => $list){
$col = 0;
$list = array(
"A"=>$list[$col++],
"B"=>$list[$col++],
"C"=>$list[$col++],
"D"=>$list[$col++],
"E"=>$list[$col++],
"F"=>$list[$col++],
"G"=>$list[$col++]
);
$data = array();
$data["no"] = $list["A"];
$data["member_info"] = $list["G"];
if(!update_column($data)){throw new Exception("엑셀 업로드 정보 수정에 실패하였습니다.");}
}
unlink($file_path);
}
echo "<script>alert('엑셀 업로드 정보 수정에 성공하였습니다.');history.back();</script>";
exit;
}catch(Exception $e){
echo "<script>alert('".$e->getMessage()."');history.back();</script>";
exit;
}
?>




<?php error_reporting(E_ALL & ~E_USER_NOTICE); ini_set("display_errors", 1); require_once 'vendors/autoload.php'; use PhpOffice\PhpSpreadsheet\IOFactory; $inputFileType = 'Xlsx'; $inputFileName = 'CashShop.xlsx'; $reader = IOFactory::createReader($inputFileType); $spreadsheet = $reader->load($inputFileName); $sheetData = $spreadsheet->getSheet(0)->toArray(null, true, true, true); print_r($sheetData); ?>

autoload.php 부터 읽어오고 이후 namespace 선언하면 class를 바로 사용할 수 있다.

$spreadsheet->getSheet(0) 하면 맨 처음 시트를 가져온다. n번째 시트를 읽도록 지정 가능.

$spreadsheet->getActiveSheet() 하면 저장하기 전 활성화 된 시트 읽기

Array ( [1] => Array ( [A] => Index [B] => Name [C] => NameCode [D] => DescCode [E] => IsSale .... ) [2] => Array ( [A] => 인덱스 [B] => 이름 [C] => 상품 이름 번호 [D] => 상품 설명 번호 [E] => 판매 여부 .... ) .... )

엑셀의 기타 정보를 입력하고 업로드했을 때 데이터 수정이 잘 되는지 확인한다.

0
0
이 글을 페이스북으로 퍼가기 이 글을 트위터로 퍼가기 이 글을 카카오스토리로 퍼가기 이 글을 밴드로 퍼가기

PHP

번호 제목 글쓴이 날짜 조회수
16 PhpSpreadsheet 설정 샘플 코드 관리자 06-21 231
15 PHP & JavaScript 엑셀 파일 다운로드 및 업로드 구현 (PhpSpreadsheet 사용) 관리자 06-21 225
14 PHP 스크립트에서 JSON 반환하기 관리자 11-26 2,481
13 PhpSpreadsheet 설치 및 사용법 정리 관리자 11-07 483
12 PHP 프로그램에서 BULK INSERT 문장을 만드는 방법 관리자 07-07 490
11 PHP 문자 암호화하여 Form 전송하기 관리자 07-07 469
10 전화번호 체크하기(휴대전화, 유선, 대표번호 등등) 관리자 07-03 424
9 get vimeo thumb 관리자 04-14 436
8 PHP 에서 MySQL 사용하기 (연결, DB&테이블 생성, 데이터 삽입/선택) 관리자 04-13 567
7 [PHP] 특정 영역 자동 스크린샷 저장 후 가장 최신 이미지 DB 저장 관리자 03-15 500
6 GD PHP TEXT ALIGN 한글 관리자 03-14 415
5 [PHP] 이미지파일에 텍스트 넣기 (라이브러리) 관리자 03-12 727
4 PHP 이미지 워터마크(텍스트&이미지) 삽입하기 관리자 03-11 412
3 한글 종성유무에 맞는 조사(은/는/이/가/을/를/과/와) 변환 관리자 06-22 651
2 한국어 조사 처리 함수 관리자 04-08 737
1 PHP 에서 callback 함수를 이용하여서 mysql select row 함수 개발하는 방법 관리자 10-22 907