참고
- https://www.npmjs.com/package/archiver
다음과 같은 경로가 있다고 가정하자.
let folderPath = "D:\\github\\node-server\\macro\\myfiles"; // 압축할 폴더 경로
해당 경로에는 아래의 폴더와 파일이 존재한다.
리액트에서 버튼을 클릭하면 위의 폴더를 zip으로 다운로드 받아보자.
리액트 예시 코드는 다음과 같다.
import React from "react";
import axios from "axios";
const DownloadZip = () => {
const handleDownload = async () => {
let server = `http://192.168.55.120:3002/file_download`;
try {
const response = await axios.get(server, {
responseType: "blob",
});
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "folder.zip");
document.body.appendChild(link);
link.click();
setTimeout(() => {
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
}, 1000);
} catch (error) {
console.error("Error:", error);
}
};
return (
<div>
<button onClick={handleDownload}>download</button>
</div>
);
};
export default DownloadZip;
download 버튼을 누르면 설정한 이름(folder.zip)으로 다운로드된다.
서버 구현
먼저 archiver 라이브러리를 설치한다.
npm install archiver
먼저 압축할 폴더 경로, 해당 경로를 압축한 zip 파일의 이름, 그리고 zip 파일을 저장할 경로를 설정한다.
압축 파일은 임시로 저장해도 되기 때문에 따로 zipPath를 만들었다.
let folderPath = "D:\\github\\node-server\\macro\\myfiles"; // 압축할 폴더 경로
let zipName = `folder_${dayjs().valueOf()}.zip`; // 압축된 zip 이름
let zipPath = `D:\\temp_download\\${zipName}`; // zip을 저장할 폴더
그리고 output과 압축 레벨을 설정한다.
const output = fs.createWriteStream(zipPath);
const archive = archiver("zip", {
zlib: { level: 9 }, // 압축 레벨 설정 (최대 압축)
});
close 이벤트에 대한 핸들러를 등록한다.
sendFile을 이용해 파일을 전송할 수 있다.
그리고 압축된 파일은 서버에 남아있을 필요가 없어서 30초 뒤에 삭제하도록 하였다.
output.on("close", function () {
console.log(archive.pointer() + " total bytes...");
res.sendFile(zipPath); // 압축된 파일 전송
setTimeout(() => {
fs.unlink(zipPath, (err) => {
if (err) {
console.error("Error deleting file:", err);
} else {
console.log(`${zipName} has been deleted.`);
}
});
}, 30000); // 30초 후에 실행
});
그리고 archive와 관련된 메서드를 추가하면 된다. (에러 처리 포함)
directory의 옵션이 true라면 절대 경로에 해당하는 폴더를 기준으로 압축 파일이 생성된다.
이 과정이 끝나야(출력 스트림이 닫힐 때), 위의 output.on의 내용이 실행된다.
archive.on("error", function (err) {
throw err;
});
archive.pipe(output); // 압축 파일 스트림을 출력 스트림(output)에 연결
archive.directory(folderPath, false); // 지정된 경로를 압축 파일에 추가
archive.finalize(); // 압축 프로세스 완료 후 압축 파일 생성
리액트에서 다운로드한 zip을 압축 해제하면 정상적으로 전송된 것을 알 수 있다.
전체 코드는 다음과 같다.
const express = require("express");
const router = express.Router();
const fs = require("fs");
const archiver = require("archiver");
const dayjs = require("dayjs");
router.get("/", (req, res) => {
let folderPath = "D:\\github\\node-server\\macro\\myfiles"; // 압축할 폴더 경로
let zipName = `folder_${dayjs().valueOf()}.zip`; // 압축된 zip 이름
let zipPath = `D:\\temp_download\\${zipName}`; // zip을 저장할 폴더
const output = fs.createWriteStream(zipPath);
const archive = archiver("zip", {
zlib: { level: 9 }, // 압축 레벨 설정 (최대 압축)
});
output.on("close", function () {
console.log(archive.pointer() + " total bytes...");
res.sendFile(zipPath); // 압축된 파일 전송
setTimeout(() => {
fs.unlink(zipPath, (err) => {
if (err) {
console.error("Error deleting file:", err);
} else {
console.log(`${zipName} has been deleted.`);
}
});
}, 30000); // 30초 후에 실행
});
archive.on("error", function (err) {
throw err;
});
archive.pipe(output); // 압축 파일 스트림을 출력 스트림(output)에 연결
archive.directory(folderPath, false); // 지정된 경로를 압축 파일에 추가
archive.finalize(); // 압축 프로세스 완료 후 압축 파일 생성
});
module.exports = router;
특정 파일만 압축하기
특정 파일만 모아서 압축하려면 append를 사용하면 된다.
예시는 다음과 같다. (archive.directory 삭제)
archive.pipe(output); // 압축 파일 스트림을 출력 스트림(output)에 연결
const file0 = "D:\\github\\node-server\\macro\\myfiles\\package.json";
const file1 = "D:\\github\\node-server\\macro\\myfiles\\markdown\\Manual.md";
archive.append(fs.createReadStream(file0), { name: "packgae.json" });
archive.append(fs.createReadStream(file1), { name: "Manual.md" });
archive.append("hello world!", { name: "my_file.txt" });
archive.finalize(); // 압축 프로세스 완료 후 압축 파일 생성
설정한 파일이 정상적으로 다운로드 되는 것을 확인해 보자.
댓글