Javascript 可恢復(fù)的文件上傳

2023-02-17 10:57 更新

使用 ?fetch? 方法來上傳文件相當(dāng)容易。

連接斷開后如何恢復(fù)上傳?這里沒有對此的內(nèi)建選項(xiàng),但是我們有實(shí)現(xiàn)它的一些方式。

對于大文件(如果我們可能需要恢復(fù)),可恢復(fù)的上傳應(yīng)該帶有上傳進(jìn)度提示。由于 fetch 不允許跟蹤上傳進(jìn)度,我們將會(huì)使用 XMLHttpRequest

不太實(shí)用的進(jìn)度事件

要恢復(fù)上傳,我們需要知道在連接斷開前已經(jīng)上傳了多少。

我們有 xhr.upload.onprogress 來跟蹤上傳進(jìn)度。

不幸的是,它不會(huì)幫助我們在此處恢復(fù)上傳,因?yàn)樗鼤?huì)在數(shù)據(jù) 被發(fā)送 時(shí)觸發(fā),但是服務(wù)器是否接收到了?瀏覽器并不知道。

或許它是由本地網(wǎng)絡(luò)代理緩沖的(buffered),或者可能是遠(yuǎn)程服務(wù)器進(jìn)程剛剛終止而無法處理它們,亦或是它在中間丟失了,并沒有到達(dá)服務(wù)器。

這就是為什么此事件僅適用于顯示一個(gè)好看的進(jìn)度條。

要恢復(fù)上傳,我們需要 確切地 知道服務(wù)器接收的字節(jié)數(shù)。而且只有服務(wù)器能告訴我們,因此,我們將發(fā)出一個(gè)額外的請求。

算法

  1. 首先,創(chuàng)建一個(gè)文件 id,以唯一地標(biāo)識(shí)我們要上傳的文件:
  2. let fileId = file.name + '-' + file.size + '-' + file.lastModified;
    

    在恢復(fù)上傳時(shí)需要用到它,以告訴服務(wù)器我們要恢復(fù)的內(nèi)容。

    如果名稱,或大小,或最后一次修改時(shí)間發(fā)生了更改,則將有另一個(gè) fileId。

  3. 向服務(wù)器發(fā)送一個(gè)請求,詢問它已經(jīng)有了多少字節(jié),像這樣:
  4. let response = await fetch('status', {
      headers: {
        'X-File-Id': fileId
      }
    });
    
    // 服務(wù)器已有的字節(jié)數(shù)
    let startByte = +await response.text();

    這假設(shè)服務(wù)器通過 X-File-Id header 跟蹤文件上傳。應(yīng)該在服務(wù)端實(shí)現(xiàn)。

    如果服務(wù)器上尚不存在該文件,則服務(wù)器響應(yīng)應(yīng)為 0

  5. 然后,我們可以使用 Blob 和 slice 方法來發(fā)送從 startByte 開始的文件:
  6. xhr.open("POST", "upload", true);
    
    // 文件 id,以便服務(wù)器知道我們要恢復(fù)的是哪個(gè)文件
    xhr.setRequestHeader('X-File-Id', fileId);
    
    // 發(fā)送我們要從哪個(gè)字節(jié)開始恢復(fù),因此服務(wù)器知道我們正在恢復(fù)
    xhr.setRequestHeader('X-Start-Byte', startByte);
    
    xhr.upload.onprogress = (e) => {
      console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`);
    };
    
    // 文件可以是來自 input.files[0],或者另一個(gè)源
    xhr.send(file.slice(startByte));

    這里我們將文件 id 作為 X-File-Id 發(fā)送給服務(wù)器,所以服務(wù)器知道我們正在上傳哪個(gè)文件,并且,我們還將起始字節(jié)作為 X-Start-Byte 發(fā)送給服務(wù)器,所以服務(wù)器知道我們不是重新上傳它,而是恢復(fù)其上傳。

    服務(wù)器應(yīng)該檢查其記錄,如果有一個(gè)上傳的該文件,并且當(dāng)前已上傳的文件大小恰好是 X-Start-Byte,那么就將數(shù)據(jù)附加到該文件。

這是用 Node.js 寫的包含客戶端和服務(wù)端代碼的示例。

在本網(wǎng)站上,它只有部分能工作,因?yàn)?Node.js 位于另一個(gè)服務(wù) Nginx 后面,該服務(wù)器緩沖(buffer)上傳的內(nèi)容,當(dāng)完全上傳后才將其傳遞給 Node.js。

但是你可以下載這些代碼,在本地運(yùn)行以進(jìn)行完整演示:

下載鏈接

正如我們所看到的,現(xiàn)代網(wǎng)絡(luò)方法在功能上已經(jīng)與文件管理器非常接近 —— 控制 header,進(jìn)度指示,發(fā)送文件片段等。

我們可以實(shí)現(xiàn)可恢復(fù)的上傳等。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號