1. 程式人生 > 其它 >logger(二)、springBoot的日誌原始碼檢視(LogBack + slf4j)——建立ILoggerFactory

logger(二)、springBoot的日誌原始碼檢視(LogBack + slf4j)——建立ILoggerFactory

一、ZIP 格式簡介

ZIP 檔案格式是一種資料壓縮和文件儲存的檔案格式,原名 Deflate,發明者為菲爾·卡茨(Phil Katz),他於 1989 年 1 月公佈了該格式的資料。ZIP 通常使用字尾名 “.zip”,它的 MIME 格式為 “application/zip”。目前,ZIP 格式屬於幾種主流的壓縮格式之一,其競爭者包括RAR 格式以及開放原始碼的 7z 格式。

ZIP 是一種相當簡單的分別壓縮每個檔案的存檔格式,分別壓縮檔案允許不必讀取另外的資料而檢索獨立的檔案。理論上,這種格式允許對不同的檔案使用不同的演算法。然而,在實際上,ZIP 大多數都是在使用卡茨(Katz)的 DEFLATE 演算法。

簡單介紹完 ZIP 格式,接下來阿寶哥先來介紹基於jsZip這個庫的瀏覽器解壓方案。

二、瀏覽器解壓方案

jsZip 是一個用於建立、讀取和編輯.zip檔案的JavaScript庫,該庫支援大多數瀏覽器,具體的相容性如下圖所示:

其實有了 JSZip 這個庫的幫助,要實現瀏覽器端線上解壓 ZIP 檔案的功能並不難。因為官方已經為我們提供了解壓本地檔案、解壓遠端檔案和生成 ZIP 檔案的完整示例。好的,廢話不多說,下面我們來一步步實現線上解壓 ZIP 檔案的功能。

2.1 定義工具類

瀏覽器端線上解壓 ZIP 檔案的功能,可以拆分為下載 ZIP 檔案、解析 ZIP 檔案和展示 ZIP 檔案

3 個小功能。考慮到功能複用性,阿寶哥把下載 ZIP 檔案和解析 ZIP 檔案的邏輯封裝在ExeJSZip類中:

classExeJSZip{
//用於獲取url地址對應的檔案內容
getBinaryContent(url,progressFn=()=>{}){
returnnewPromise((resolve,reject)=>{
if(typeofurl!=="string"||!/https?:/.test(url))
reject(newError("url引數不合法"));
JSZipUtils.getBinaryContent(url,{//JSZipUtils來自於jszip-utils這個庫
progress:progressFn,
callback:(err,data)=>{
if(err){
reject(err);
}else{
resolve(data);
}
},
});
});
}

//遍歷Zip檔案
asynciterateZipFile(data,iterationFn){
if(typeofiterationFn!=="function"){
thrownewError("iterationFn不是函式
型別");
}
letzip;
try{
zip=awaitJSZip.loadAsync(data);//JSZip來自於jszip這個庫
zip.forEach(iterationFn);
returnzip;
}catch(error){
thrownewerror();
}
}
}

2.2 線上解壓 ZIP 檔案

利用ExeJSZip類的例項,我們就可以很容易實現線上解壓 ZIP 檔案的功能:

html程式碼
<p>
<label>請輸入ZIP檔案的線上地址:</label>
<inputtype="text"id="zipUrl"/>
</p>
<buttonid="unzipBtn"onclick="unzipOnline()">線上解壓</button>
<pid="status"></p>
<ulid="fileList"></ul>
JS 程式碼
constzipUrlEle=document.querySelector("#zipUrl");
conststatusEle=document.querySelector("#status");
constfileList=document.querySelector("#fileList");
constexeJSZip=newExeJSZip();

//執行線上解壓操作
asyncfunctionunzipOnline(){
fileList.innerhtml="";
statusEle.innerText="開始下載檔案...";
constdata=awaitexeJSZip.getBinaryContent(
zipUrlEle.value,
handleProgress
);
letitems="";
awaitexeJSZip.iterateZipFile(data,(relativePath,zipEntry)=>{
items+=`<liclass=${zipEntry.dir?"caret":"indent"}>
${zipEntry.name}</li>`;
});
statusEle.innerText="ZIP檔案解壓成功";
fileList.innerHTML=items;
}

//處理下載進度
functionhandleProgress(progressData){
const{percent,loaded,total}=progressData;
if(loaded===total){
statusEle.innerText="檔案已下載,努力解壓中";
}
}

好了,在瀏覽器端如何通過 JSZip 這個庫來實現線上解壓 ZIP 檔案的功能已經介紹完了,我們來看一下以上示例的執行結果:

現在我們已經可以線上解壓 ZIP 檔案了,這時有的小夥伴可能會問,能否預覽解壓後的檔案呢?答案是可以的,因為 JSZip 這個庫為我們提供了fileAPI,通過這個 API 我們就可以讀取指定檔案中的內容。比如這樣使用zip.file("amount.txt").async("arraybuffer"),之後我們就可以執行對應的操作來實現檔案預覽的功能。

需要注意的是,基於 JSZip 的方案並不是完美的,它存在一些限制。比如它不支援解壓加密的 ZIP 檔案,當解壓較大的檔案時,在 IE 10 以下的瀏覽器可能會出現閃退問題。此外,它還有一些其它的限制,這裡阿寶哥就不詳細說明了。感興趣的小夥伴,可以閱讀Limitations of JSZip文章中的相關內容。

既然瀏覽器解壓方案存在一些弊端,特別是線上解壓大檔案的情形,要解決該問題,我們可以考慮使用伺服器解壓方案。

https://www.98891.com/article-76-1.html

三、伺服器解壓方案

伺服器解壓方案就是允許使用者通過檔案 ID 或檔名進行線上解壓,接下來阿寶哥將基於 koa 和 node-stream-zip 這兩個庫來介紹如何實現伺服器線上解壓 ZIP 檔案的功能。如果你對 koa 還不瞭解的話,建議你先大致閱讀一下 koa 的官方文件。

constpath=require("path");
constKoa=require("koa");
constcors=require("@koa/cors");
constRouter=require("@koa/router");
constStreamZip=require("node-stream-zip");

constapp=newKoa();
constrouter=newRouter();
constZIP_HOME=path.join(__dirname,"zip");//ZIP檔案的根目錄
constUnzipCaches=newMap();//儲存已解壓的檔案資訊

router.get("/",async(ctx)=>{
ctx.body="服務端線上解壓ZIP檔案示例(阿寶哥)";
});

//註冊中介軟體
app.use(cors());
app.use(router.routes()).use(router.allowedMethods());

app.listen(3000,()=>{
console.log("appstartingatport3000");
});

在以上程式碼中,我們使用了@koa/cors和@koa/router兩個中介軟體並建立了一個簡單的 Koa 應用程式。基於上述的程式碼,我們來註冊一個用於處理線上解壓指定檔名的路由。

3.1 根據檔名解壓指定 ZIP 檔案

app.js
router.get("/unzip/:name",async(ctx)=>{
constfileName=ctx.params.name;
letfilteredEntries;
try{
if(UnzipCaches.has(fileName)){//優先從快取中獲取
filteredEntries=UnzipCaches.get(fileName);
}else{
constzip=newStreamZip.async({file:path.join(ZIP_HOME,fileName)});
constentries=awaitzip.entries();
filteredEntries=Object.values(entries).map((entry)=>{
return{
name:entry.name,
size:entry.size,
dir:entry.isDirectory,
};
});
awaitzip.close();
UnzipCaches.set(fileName,filteredEntries);
}
ctx.body={
status:"success",
entries:filteredEntries,
};
}catch(error){
ctx.body={
status:"error",
msg:`線上解壓${fileName}檔案失敗`,
};
}
});

在以上程式碼中,我們通過ZIP_HOME和fileName獲得檔案的最終路徑,然後使用StreamZip物件來執行解壓操作。為了避免重複執行解壓操作,阿寶哥定義了一個UnzipCaches快取物件,用來儲存已解壓的檔案資訊。定義好上述路由,下面我們來驗證一下對應的功能。

3.2 線上解壓 ZIP 檔案

html 程式碼
<p>
<label>請輸入ZIP檔名:</label>
<inputtype="text"id="fileName"value="kl_161828427993677"/>
</p>
<buttonid="unzipBtn"onclick="unzipOnline()">線上解壓</button>
<pid="status"></p>
<ulid="fileList"></ul>
JS 程式碼
constfileList=document.querySelector("#fileList");
constfileNameEle=document.querySelector("#fileName");

constrequest=axios.create({
baseURL:"http://localhost:3000/",
timeout:10000,
});

asyncfunctionunzipOnline(){
constfileName=fileNameEle.value;
if(!fileName)return;
constresponse=awaitrequest.get(`unzip/${fileName}`);
if(response.data&&response.data.status==="success"){
constentries=response.data.entries;
letitems="";
entries.forEach((zipEntry)=>{
items+=`<liclass=${zipEntry.dir?"caret":"indent"}>${
zipEntry.name
}</li>`;
});
fileList.innerHTML=items;
}
}

以上示例成功執行後的結果如下圖所示:

現在我們已經實現根據檔名解壓指定 ZIP 檔案,那麼我們可以預覽壓縮檔案中指定路徑的檔案麼?答案也是可以的,利用zip物件提供的entryData(entry: string | ZipEntry): Promise<Buffer>方法就可以讀取指定路徑下檔案的內容。

3.3 預覽 ZIP 檔案中指定路徑的檔案

app.js
router.get("/unzip/:name/entry",async(ctx)=>{
constfileName=ctx.params.name;//ZIP壓縮檔名
constentryPath=ctx.query.path;//檔案的路徑
try{
constzip=newStreamZip.async({file:path.join(ZIP_HOME,fileName)});
constentryData=awaitzip.entryData(entryPath);
awaitzip.close();
ctx.body={
status:"success",
entryData:entryData,
};
}catch(error){
ctx.body={
status:"error",
msg:`讀取${fileName}中${entryPath}檔案失敗`,
};
}
});

在以上程式碼中,我們通過zip.entryData方法來讀取指定路徑的檔案內容,它返回的是一個Buffer物件。當前端接收到該資料時,還需要把接收到的Buffer物件轉換為ArrayBuffer物件,對應的處理方式如下所示:

functiontoArrayBuffer(buf){
letab=newArrayBuffer(buf.length);
letview=newUint8Array(ab);
for(leti=0;i<buf.length;++i){
view[i]=buf[i];
}
returnab;
}

定義完toArrayBuffer函式之後,我們就可以通過呼叫app.js定義的 API 來實現預覽功能,具體的程式碼如下所示:

asyncfunctionpreviewZipFile(path){
constfileName=fileNameEle.value;//獲取檔名
constresponse=awaitrequest.get(
`unzip/${fileName}/entry?path=${path}`
);
if(response.data&&response.data.status==="success"){
const{entryData}=response.data;
constentryBuffer=toArrayBuffer(entryData.data);
constblob=newBlob([entryBuffer]);
//使用URL.createObjectURL或blob.text()讀取檔案資訊
}
}