Never give up

React - zip, unzip(Feat. JSZip) 본문

WEB

React - zip, unzip(Feat. JSZip)

대기만성 개발자 2024. 11. 14. 14:21
반응형

압축 파일을 다룰일이 생겨서 간단하게 예제를 만들어봤습니다

 

먼저 클라이언트에서 압축파일을 사용하게 됐을 때의 장점으로는

1. 클라이언트가 여러개의 파일을 다운로드 받지 않아도 된다

2. 서버가 파일 리스트를 조회 한 후 넘겨주지 않아도 된다

3. 네트워크 리소스를 적게 사용한다

4. 별도의 서버작업 없이 파일 압축이 가능하다

 

정도 될거 같습니다

 

zipUtil.ts

import { saveAs } from "file-saver";
import JSZip, { JSZipObject } from "jszip";

export const zipUtil = {
  /** - extract file list from file url */
  async extractFile(url: string) {
    const res = await fetch(url);

    const arrayBuffer = await res.arrayBuffer();

    const zip = new JSZip();

    const contents = await zip.loadAsync(arrayBuffer);

    let fileList: JSZipObject[] = [];

    for (const content of Object.values(contents.files)) {
      fileList = [...fileList, content];
    }

    return fileList;
  },
  /** - zip files */
  async zipFile(fileList: { name: string; file: Blob }[], fileName: string) {
    const zip = new JSZip();

    fileList.map((e) => {
      zip.file(e.name, e.file);
    });

    const downloadFile = await zip.generateAsync({ type: "blob" });

    saveAs(downloadFile, fileName);
  },
  /** - get file from url */
  async getFileFromUrl(url: string) {
    const res = await fetch(url);
    return await res.blob();
  },
};

구현한 함수는 총 3개로

1. zip파일을 받아서 압축 해제하는 부분

2. 압축하기

3. url로 부터 파일 blob형태로 가져오기

 

간단하게 만들어 봤습니다

 

해당부분을 사용하는 컴포넌트 부분에서 같이 코드를 보면 좋을거 같습니다

import { JSZipObject } from "jszip";
import React, { useState } from "react";
import Button from "src/component/button";
import { zipUtil } from "src/util/zipUitl";
import styles from "./zip.module.css";

const ZipPage = () => {
  const [images, setImages] = useState<string[]>([]);

  const onUnzipFile = async () => {
    // public/assets/example.zip
    const files = await zipUtil.extractFile("/example.zip");

    let imgList: string[] = [];

    // remove dir
    for (const e of files.slice(1)) {
      const img = await toImg(e);
      imgList = [...imgList, img];
    }

    setImages(imgList);
  };

  const onDownload = async () => {
    let fileList: { name: string; file: Blob }[] = [];

    for (const name of ["react.svg", "node.svg"]) {
      const file = await zipUtil.getFileFromUrl(`/assets/${name}`);

      fileList = [...fileList, { name, file }];
    }

    zipUtil.zipFile(fileList, "example.zip");
  };

  const toImg = async (obj: JSZipObject) => {
    const blob = await obj.async("blob");
    // never set it as octet-stream if you want to show it in img tag
    const file = new File([blob], obj.name, { type: "image/svg+xml" });

    return new Promise<string>((res) => {
      const reader = new FileReader();

      reader.onload = () => {
        res(`${reader.result}`);
      };

      reader.readAsDataURL(file);
    });
  };

  return (
    <div className={styles.div_main}>
      <div>
        <Button onClick={onUnzipFile}>Unzip</Button>
        <Button onClick={onDownload}>Download</Button>
      </div>
      <div className={images.length === 0 ? styles.hidden : undefined}>
        {images.map((e, i) => {
          return <img key={`img-${i}`} width={50} height={50} src={e} />;
        })}
      </div>
    </div>
  );
};

export default ZipPage;

먼저 unZipFile부터 보면 .zip파일(이미지 파일들 압축)을 불러와서

 

blob형태로 로드 해준 후 File로 변환 -> 이미지로 변환 해주는 작업을 하고 있습니다

 

createObjectUrl이랑 FileReader부분에 대해서 장단점을 고민해본적이 있는데

 

createObjectUrl

1. 대용량 파일에 적합 ex) 이미지, 동영상, 음성파일

2. 메모리를 조금 더 효율적으로 사용

3. revoke를 해줘야됨

 

FileReader

1. base64, binary등의 데이터를 다룰때 적합

2. 대용량 파일을 불러올 때 퍼포먼스 이슈 발생 가능

3. 별도로 revoke를 안해줘도됨

 

해당 예제에서는 작은 svg를 사용하기에 FIleReader가 조금 더 나을거 같아서 사용해봤습니다

(revoke하기 귀찮아서..)

 

다음으로 download 부분을 보면

 

public/asset에 있는 파일들을 가져와서 압축하고 다운로드 하는 부분인데

 

다른 서버 파일 url을 사용할때도 동일합니다

 

zip에 파일을 넣을때 fileName, file(blob)형태로 들어가게 됩니다

< zip/unzip example >

반응형
Comments