天天看點

前端批量擷取檔案并打包壓縮解決方案

前端批量擷取檔案并打包壓縮解決方案

作者 | xmanlin

前言​

前端檔案下載下傳我相信很多小夥伴并不陌生,下載下傳檔案的形式也有很多,例如,後端傳回一個檔案位址,我們把位址放在<a></a>标簽裡面點選下載下傳;或者是通過後端接口傳回檔案流,我們再對流進行一系列的操作等等。

單個件下載下傳的解決方法有很多,但是當我們需要批量下載下傳檔案的時候,我們該怎麼去做呢?

方案​

面對這樣的需求,我們提出了以下幾個方案:

方案一:直接擷取後端傳回檔案位址數組,然後一個一個的去下載下傳。但是這樣每次下載下傳一個檔案,浏覽器會顯示比較多的下載下傳任務;

方案二:後端對先對檔案進行打包壓縮處理,然後前端隻需要下載下傳一個壓縮檔案,但是這樣會對伺服器性能造成很大的影響;

方案三:還是直接擷取後端傳回的檔案位址數組,一個一個的去下載下傳,然後前端來進行打包壓縮的處理。

說實話,當時提到前端來打包壓縮時,我心中就和一部分小夥伴一樣,前端怎麼打包壓縮?下面這兩個優秀的庫就可以很好的解決我們的問題。

這裡提一下,下面是以react環境下為例,但是在其他環境的思路和用法其實都是大同小異,都可以以下面的内容為參考。

jsZip和FileSaver.js

本節會簡單的介紹一下JSZip和FileSaver.js的API和用法。

安裝

npm install jszip file-saver      

JSZip​

JSZip是一個用于建立、讀取和編輯.zip檔案的JavaScript庫,并且擁有有友好而簡單的API。

一個簡單的例子​

首先我們來實作一個簡單的例子,來感受一下這個十分好用的工具

import React , { useState } from 'react';
import JSZip from 'jszip';
import FileSaver from 'file-saver'; 
const MyButton = () => {


    const downloadFile = () => {
        const zip = new JSZip();
        zip.file("Hello.txt", "Hello World\n");
        zip.generateAsync({type:"blob"})
        .then((content) => { 
            FileSaver(content, "example.zip");
        });
    }
    return (
        <div>
            <button onClick={() => {
                downloadFile()
            }}>下載下傳</button>
        </div>
    )
} 
export default MyButton      

點選下載下傳按鈕,我們就可以得到一個名為example.zip的壓縮檔案,打開壓縮檔案,裡面也會有一個名為Hello.txt的檔案.

API​

簡單介紹一下幾個API。

建立JSZip執行個體:

const zip = new JSZip();      

建立檔案:

zip.file("hello.txt", "Hello World\n");      

建立檔案夾:

zip.folder("file")      

同時建立檔案夾和檔案:

zip.file("file/hello.txt", "Hello World\n");
// 等同于
zip.folder("file").file("hello.txt", "Hello World\n");      

生成一個壓縮檔案:​

我們可以通過.generateAsync(options) 或者 .generateNodeStream(options) 來生成一個壓縮檔案:

let promise = null;
if (JSZip.support.uint8array) {
  promise = zip.generateAsync({type : "uint8array"});
} else {
  promise = zip.generateAsync({type : "string"});
}      

FileSaver.js​

在前面的這個例子中我們運用了JSZip外還使用了FileSaver.js這個庫。FileSaver.js是在用戶端儲存檔案的解決方案,非常适合在用戶端生成檔案。

在上一節的例子中,我們就是通過FileSaver.js把我們生成的.zip檔案儲存了下來。

文法​
FileSaver saveAs(Blob/File/Url, optional DOMString filename, optional Object { autoBom })      
例子​
import FileSaver from 'file-saver';


const blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
FileSaver.saveAs(blob, "hello world.txt");      

批量擷取檔案并打包下載下傳​

這兩個庫我們已經有所了解接下來就是實作我們的需求。這裡分兩步進行,第一步是擷取檔案;第二步是打包壓縮。

需要操作的源檔案位址​

這裡的檔案位址隻是一個簡單的示例,實際開發的時候視情況而定。​

const data = [
    {
        fileUrl: 'https://www.xxx.com/data/data_service/20210429/144b4b1e4e457485c10fed54b8bc8d48.docx',
        fileName: '檔案一'
    },
    {
        fileUrl: 'https://www.xxx.com/data/data_service/20210429/144b4b1e4e457485c10fed54b8bc8d48.docx',
        fileName: '檔案二'
    },
    {
        fileUrl: 'https://www.xxx.com/data/data_service/20210429/144b4b1e4e457485c10fed54b8bc8d48.docx',
        fileName: '檔案三'
    },
    {
        fileUrl: 'https://www.xxx.com/data/data_service/20210429/144b4b1e4e457485c10fed54b8bc8d48.docx',
        fileName: '檔案四'
    },
];      

擷取檔案

import JSZip from 'jszip';
import FileSaver from 'file-saver';
import requestFile from './requestFile'; //這裡是封裝的請求函數,大家用自己封裝的或者Axios都行


const getFile = (url: string) => {
  return new Promise((resolve, reject) => {
    requestFile(url, {
      method: 'GET',
      responseType: 'blob'
    }).then((res:any) => {
      resolve(res)
    }).catch((error: any) => {
      reject(error)
    })
  })
}      

打包壓縮下載下傳​

這裡主要是通過周遊位址數組,然後通過位址從後端擷取檔案,再進行一個批量壓縮打封包件的操作,最後把壓縮好的檔案儲存下來。​

/**
 * 打包壓縮下載下傳
 * @param data  源檔案數組
 * @param fileName  壓縮檔案的名稱
 */
const compressAndDownload = (data: any[], fileName ?: string) => {
  const zip = new JSZip();
  const promises: any[] = [];  //用于存儲多個promise
  data.forEach((item: any) => {
    const promise = getFile(item.fileUrl).then((res: any) => {
      const fileName = item.fileName
      zip.file(fileName, res ,{binary: true});
    })
    promises.push(promise)
  })


  Promise.all(promises).then(() => {
    zip.generateAsync({
      type: "blob",
      compression: "DEFLATE",  // STORE:預設不壓縮 DEFLATE:需要壓縮
      compressionOptions: {
        level: 9               // 壓縮等級1~9    1壓縮速度最快,9最優壓縮方式
      }
    }).then((res: any) => {
      FileSaver.saveAs(res, fileName ? fileName : "壓縮包.zip") // 利用file-saver儲存檔案
    })
  })
}


export default compressAndDownload;      

最後​