最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Node.js實現(xiàn)大文件斷點續(xù)傳的完整方案

 更新時間:2026年04月03日 09:12:22   作者:鄒榮樂  
本文介紹了Vue前端和Node.js后端實現(xiàn)大文件斷點續(xù)傳的方法,前端通過分片上傳、計算文件哈希、記錄已上傳分片索引實現(xiàn)斷點續(xù)傳;后端提供檢查和上傳分片、合并文件的接口,通過并發(fā)控制、錯誤重試等技術(shù)提高上傳效率和穩(wěn)定性,需要的朋友可以參考下

在 Vue 前端開發(fā)中實現(xiàn)大文件斷點續(xù)傳,需結(jié)合分片上傳、哈希校驗和進度恢復(fù)等技術(shù)。以下是具體實現(xiàn)方案:

一、技術(shù)原理

  • 分片上傳:將大文件切割為多個小塊(如 1MB/片)。
  • 唯一標識:計算文件哈希(如 MD5)作為文件唯一標識。
  • 斷點記錄:記錄已上傳的分片索引,中斷后從斷點繼續(xù)上傳。
  • 合并請求:所有分片上傳完成后,通知后端合并文件。
  • 并發(fā)控制:p-limit 是一個控制請求并發(fā)數(shù)量的庫。

1、p-limit

npm install p-limit

p-limit是一個用于限制并發(fā)操作的JavaScript庫,主要用于控制同時執(zhí)行的異步操作數(shù)量,以避免系統(tǒng)資源過度占用和性能下降。它通過維護一個請求隊列來管理并發(fā)操作,當(dāng)并發(fā)限制數(shù)未滿時,新操作會立即執(zhí)行;當(dāng)達到限制數(shù)時,新操作會被加入等待隊列,直到有空閑位置再執(zhí)行。

使用方法
使用p-limit非常簡單,首先需要創(chuàng)建一個限制對象并指定并發(fā)限制數(shù)。然后將需要執(zhí)行的異步操作包裝成一個函數(shù),并通過調(diào)用限制對象的函數(shù)來執(zhí)行這些操作。當(dāng)并發(fā)限制數(shù)未滿時,操作會立即執(zhí)行;如果達到限制數(shù),操作會被加入等待隊列,按照先進先出的順序執(zhí)行

2、spark-md5

npm install spark-md5

Spark-md5是一個JavaScript庫,用于快速計算文件或數(shù)據(jù)的MD5值,支持瀏覽器環(huán)境,可用于文件完整性校驗和分片計算。其主要功能包括calculate、append、init和end等方法,適用于大文件分片處理。由于MD5是單向哈希,故無法解密。通過比較文件的MD5值,可以判斷文件是否一致。

二、實現(xiàn)步驟

1、文件分片

// 使用 File.slice 方法分片
const chunkSize = 1 * 1024 * 1024; // 1MB
const chunks = [];
let start = 0;
while (start < file.size) {
  const end = Math.min(start + chunkSize, file.size);
  const chunk = file.slice(start, end);
  chunks.push(chunk);
  start = end;
}

2、計算文件哈希

/**
 * 計算文件的哈希值
 * @param file 文件對象
 * @returns Promise<string> 返回計算得到的哈希值
 */
const calculateFileHash = async (file) => {
  return new Promise((resolve) => {
    const spark = new SparkMD5.ArrayBuffer();
    const reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = (e) => {
      spark.append(e.target.result);
      resolve(spark.end());
    };
  });
};

3、查詢已上傳分片

file.value = uploadFile.raw;
fileHash.value = await calculateFileHash(file.value);
const { data } = await axios.get('/api/check', {
    params: { fileHash: fileHash.value }
});

4、上傳分片

    // 上傳未完成的分片
    const requests = chunks.map((chunk, index) => {
      if (uploadedChunkIndexes.value.includes(index)) return;

      const formData = new FormData();
      formData.append('chunk', chunk);
      formData.append('hash', fileHash.value);
      formData.append('index', index);

      return axios.post('/api/upload', formData, {
        onUploadProgress: (e) => {
          const percent = Math.round((e.loaded / e.total) * 100);
          updateProgress(index, percent);
        }
      }).then(() => {
        uploadedChunkIndexes.value.push(index);
        updateProgress();
      });
    }).filter(Boolean);

    await Promise.all(requests);

5、并發(fā)控制

// 引入 p-limit
import pLimit from 'p-limit';

// 設(shè)置最大并發(fā)數(shù)(例如 3)
const CONCURRENT_LIMIT = 3;

// 創(chuàng)建并發(fā)控制器
const limit = pLimit(CONCURRENT_LIMIT);

    // 生成任務(wù)列表(過濾已上傳的分片)
    const tasks = chunks.map((chunk, index) => {
      if (uploadedChunkIndexes.value.includes(index)) {
        return Promise.resolve(); // 跳過已上傳
      }

      // 將每個任務(wù)包裹在并發(fā)控制器中
      return limit(() => {
        const formData = new FormData();
        formData.append('chunk', chunk);
        formData.append('hash', fileHash.value);
        formData.append('index', index);

        return axios.post('/api/upload', formData, {
          onUploadProgress: (e) => {
          // 更新進度條(需結(jié)合 Vue 響應(yīng)式狀態(tài))
          }
        }).then(() => {
          // 更新已上傳的分片索引列表
          uploadedChunkIndexes.value.push(index);
        });
      });
    });

// 等待所有任務(wù)完成
await Promise.all(tasks);

6、通知合并文件

await axios.post('/api/merge', {
      fileName: file.value.name,
      fileHash: fileHash.value,
      chunkCount: chunks.length
});

5、錯誤重試

單個分片上傳失敗時自動重試(如 3 次)。

return limit(() => {
  const MAX_RETRY = 3;
  let retryCount = 0;

  const attemptUpload = () => {
    return axios.post('/api/upload').catch((error) => {
      if (retryCount < MAX_RETRY) {
        retryCount++;
        return attemptUpload();
      }
      throw error;
    });
  };

  return attemptUpload();
});

前端所有代碼

7、前端頁面

<template>
  <div class="upload-container">
    <!-- 文件選擇 -->
    <el-upload class="upload-demo" drag :auto-upload="false" :on-change="handleFileChange" :show-file-list="false">
      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
      <div class="el-upload__text">將文件拖到此處或<em>點擊選擇</em></div>
    </el-upload>
    <!-- 上傳進度 -->
    <div v-if="file" class="progress-box">
      <div class="file-info">
        {{ file.name }} ({{ formatSize(file.size) }})
      </div>
      <el-progress :percentage="totalProgress" :status="uploadStatus" :stroke-width="16" />
      <div class="action-buttons">
        <el-button type="primary" @click="startUpload" :disabled="isUploading || isMerging">
          {{ isUploading ? '上傳中...' : '開始上傳' }}
        </el-button>
        <el-button @click="pauseUpload" :disabled="!isUploading">
          暫停
        </el-button>
      </div>
    </div>
  </div>
</template>

三、后端實現(xiàn)

以下是一個基于 Node.js (Express) 實現(xiàn)大文件斷點續(xù)傳后端的完整方案,與前端的 Vue + Element UI 示例完美配合:

  • 分片上傳的接口:接收前端傳來的每個分片,存儲到臨時目錄。
  • 檢查分片接口:告訴前端哪些分片已經(jīng)上傳,避免重復(fù)上傳。
  • 合并分片接口:當(dāng)所有分片上傳完成后,合并成完整的文件。
  • 文件校驗:確保文件在傳輸過程中沒有損壞,比如使用哈希校驗。
  • 臨時文件管理:上傳過程中保存分片,合并后清理臨時文件。

1、項目結(jié)構(gòu)

├── server.js          # 主入口文件
├── uploads            # 上傳文件存儲目錄
│   ├── temp           # 分片臨時存儲目錄
│   └── merged         # 合并后的文件目錄
└── package.json

2、依賴安裝

npm install express multer cors fs-extra

3、服務(wù)端代碼

// server.js

const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');
const cors = require('cors');
const fse = require('fs-extra');

const app = express();
app.use(cors());
app.use(express.json());

// 文件存儲目錄
const UPLOAD_DIR = path.resolve(__dirname, 'uploads');
const TEMP_DIR = path.join(UPLOAD_DIR, 'temp');    // 分片臨時目錄
const MERGED_DIR = path.join(UPLOAD_DIR, 'merged');// 合并文件目錄

// 確保目錄存在
fse.ensureDirSync(TEMP_DIR);
fse.ensureDirSync(MERGED_DIR);

// 分片上傳中間件
const chunkUpload = multer({ dest: TEMP_DIR });

// ================== 接口實現(xiàn) start ================== //

// 1. 檢查分片狀態(tài)
app.get('/api/check', (req, res) => {
  const { fileHash } = req.query;
  const chunkDir = path.resolve(TEMP_DIR, fileHash);

  // 已上傳的分片索引列表
  let uploadedChunks = [];
  if (fse.existsSync(chunkDir)) {
    uploadedChunks = fse.readdirSync(chunkDir).map(name => parseInt(name));
  }

  res.json({
    code: 0,
    data: {
      uploadedIndexes: uploadedChunks
    }
  });
});

// 2. 上傳分片
app.post('/api/upload', chunkUpload.single('chunk'), async (req, res) => {
  const { hash, index } = req.body;
  const chunkPath = req.file.path;
  const chunkDir = path.resolve(TEMP_DIR, hash);

  try {
    // 創(chuàng)建哈希目錄
    await fse.ensureDir(chunkDir);
    
    // 移動分片到目標目錄(使用索引命名)
    const destPath = path.join(chunkDir, index);
    await fse.move(chunkPath, destPath);

    res.json({ code: 0, message: '分片上傳成功' });
  } catch (err) {
    res.status(500).json({ code: 1, message: '分片上傳失敗' });
  }
});

// 3. 合并文件
app.post('/api/merge', async (req, res) => {
  const { fileName, fileHash, chunkCount } = req.body;
  const chunkDir = path.join(TEMP_DIR, fileHash);
  const filePath = path.join(MERGED_DIR, fileName);

  try {
    // 檢查分片是否完整
    const chunkPaths = await fse.readdir(chunkDir);
    if (chunkPaths.length !== chunkCount) {
      return res.status(400).json({ code: 1, message: '分片數(shù)量不匹配' });
    }

    // 創(chuàng)建寫入流
    const writeStream = fs.createWriteStream(filePath);
    
    // 按索引順序合并
    for (let i = 0; i < chunkCount; i++) {
      const chunkPath = path.join(chunkDir, i.toString());
      const buffer = await fse.readFile(chunkPath);
      writeStream.write(buffer);
    }

    writeStream.end();
    
    // 清理臨時目錄
    await fse.remove(chunkDir);

    res.json({ code: 0, message: '文件合并成功', data: { path: filePath } });
  } catch (err) {
    res.status(500).json({ code: 1, message: '文件合并失敗' });
  }
});

// ================== 接口實現(xiàn) end ================== //


// 定時清理臨時目錄(可選)
setInterval(() => {
  fse.emptyDirSync(TEMP_DIR);
}, 1000 * 60 * 60); // 每小時清理一次


// 啟動服務(wù)
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

3、后端接口

接口地址方法參數(shù)功能
/api/checkGETfileHash查詢已上傳分片索引
/api/uploadPOSTchunk, hash, index上傳分片
/api/mergePOSTfileName, fileHash, chunkCount合并文件

四、實現(xiàn)效果

當(dāng)用戶選擇文件時,計算文件的哈希值作為唯一標識,并檢查哪些分片已經(jīng)上傳。返回已上傳的片段。

文件上傳,并發(fā)數(shù)為3,實現(xiàn)分段上傳。

文件中途失敗后,會重試并再次上傳。

如果用戶關(guān)閉了瀏覽器或刷新了頁面,當(dāng)用戶再次選擇文件時,通過計算哈希值并與服務(wù)器上的記錄進行比較,以確定哪些分片已經(jīng)上傳。檢查服務(wù)器上的上傳狀態(tài)來恢復(fù)上傳。

文件上傳成功,并完成文件合并

五、代碼上傳

詳細的全部代碼已上傳,可以去頂部下載。

1、安裝前端依賴

切換到前端目錄web

npm i

2、啟動前端

npm run dev

3、安裝服務(wù)端依賴

切換到服務(wù)端目錄node

npm i

4、啟動服務(wù)端

npm run serve

通過此示例,可以快速實現(xiàn)一個帶友好交互的大文件斷點續(xù)傳功能,結(jié)合 Element Plus 的 UI 組件保證用戶體驗一致性。

以上就是基于Node.js實現(xiàn)大文件斷點續(xù)傳的完整方案的詳細內(nèi)容,更多關(guān)于Node.js大文件斷點續(xù)傳的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Node快速切換版本、版本回退(降級)、版本更新(升級)

    Node快速切換版本、版本回退(降級)、版本更新(升級)

    這篇文章主要介紹了Node快速切換版本、版本回退(降級)、版本更新(升級),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Node.js中,在cmd界面,進入退出Node.js運行環(huán)境的方法

    Node.js中,在cmd界面,進入退出Node.js運行環(huán)境的方法

    今天小編就為大家分享一篇Node.js中,在cmd界面,進入退出Node.js運行環(huán)境的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Node.js與MySQL交互操作及其注意事項

    Node.js與MySQL交互操作及其注意事項

    這篇文章給大家主要介紹了Node.js與MySQL交互操作及其注意事項,非常的詳細,有相同需求的小伙伴可以參考下
    2016-10-10
  • 詳解nodejs中的process進程

    詳解nodejs中的process進程

    這篇文章主要介紹了nodejs中的process進程,非常不錯,具有參考借鑒價值,需要的朋友參考下
    2017-03-03
  • node.js集成百度UE編輯器

    node.js集成百度UE編輯器

    這篇文章主要介紹了node.js集成百度UE編輯器的方法,需要的朋友可以參考下
    2015-02-02
  • node版本管理工具n包使用教程詳解

    node版本管理工具n包使用教程詳解

    這篇文章主要介紹了node版本管理工具n包使用教程詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • NodeJS配置CORS實現(xiàn)過程詳解

    NodeJS配置CORS實現(xiàn)過程詳解

    這篇文章主要介紹了NodeJS配置CORS實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • Node.js 中使用 async 函數(shù)的方法

    Node.js 中使用 async 函數(shù)的方法

    async是一個流程控制工具包,提供了直接而強大的異步功能?;贘avascript為Node.js設(shè)計,同時也可以直接在瀏覽器中使用。
    2017-11-11
  • node.js超時timeout詳解

    node.js超時timeout詳解

    本文介紹了nodejs中超時timeout事件,并給出了詳細的示例分析,非常的詳盡,推薦給需要的小伙伴參考下
    2014-11-11
  • Node.js同時安裝多個版本及相關(guān)配置指南(簡單易操作)

    Node.js同時安裝多個版本及相關(guān)配置指南(簡單易操作)

    在實際開發(fā)過程中我們可能需要安裝多個版本的 nodejs,下面這篇文章主要給大家介紹了關(guān)于Node.js同時安裝多個版本及相關(guān)配置的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-11-11

最新評論

怀安县| 沾化县| 四平市| 巫溪县| 肃南| 元氏县| 萍乡市| 乌拉特后旗| 临泉县| 汉川市| 汉沽区| 赫章县| 西乌珠穆沁旗| 郸城县| 山阴县| 孝感市| 呼伦贝尔市| 达孜县| 伊春市| 丹棱县| 鹿泉市| 吉安市| 宜昌市| 邯郸县| 梅河口市| 永兴县| 广元市| 丹棱县| 九龙城区| 宣化县| 德钦县| 荔浦县| 顺昌县| 潜山县| 宁晋县| 嵩明县| 新余市| 隆子县| 兴安县| 贺兰县| 建瓯市|