React+Node.js實現(xiàn)大文件傳輸與斷點續(xù)傳
簡述
使用React前端和Node.js后端實現(xiàn)大文件傳輸和斷點續(xù)傳的功能。通過分片上傳技術(shù),可以有效地解決網(wǎng)絡(luò)不穩(wěn)定帶來的傳輸中斷問題。
前端實現(xiàn)(React)
首先,您需要在前端項目中安裝axios、spark-md5庫以處理HTTP請求??梢允褂靡韵旅睿?/p>
npm i axios spark-md5
以下是實現(xiàn)大文件上傳的React組件代碼:
import axios from 'axios';
import { useRef, useState } from 'react';
// 定義文件分片大?。ɡ?5MB)
const CHUNK_SIZE = 5 * 1024 * 1024;
/** 解析當(dāng)前頁面功能 */
export default function FileUploader() {
const [file, setFile] = useState(null); // 用于存儲用戶選擇的文件
const [uploadProgress, setUploadProgress] = useState(0); // 上傳進(jìn)度
const uploading = useRef(false); // 用于防止重復(fù)觸發(fā)上傳邏輯
// 當(dāng)用戶選擇文件時觸發(fā)
const handleFileChange = (e) => {
setFile(e.target.files[0]); // 將選擇的文件存入狀態(tài)
setUploadProgress(0); // 重置上傳進(jìn)度
};
// 計算文件的唯一標(biāo)識 (哈希)
const calculateFileHash = async (file) => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const sparkMD5 = require('spark-md5');
const hash = sparkMD5.ArrayBuffer.hash(e.target.result);
resolve(hash);
};
reader.readAsArrayBuffer(file);
});
};
// 開始文件上傳
const handleUpload = async () => {
if (!file || uploading.current) return; // 如果未選擇文件或正在上傳,則直接返回
uploading.current = true; // 標(biāo)記為正在上傳
const fileHash = await calculateFileHash(file); // 獲取文件的唯一標(biāo)識(哈希值)
console.log('fileHash', fileHash);
const totalChunks = Math.ceil(file.size / CHUNK_SIZE); // 計算文件分片總數(shù)
// 檢查哪些分片已經(jīng)上傳
const { data: uploadedChunks } = await axios.post(
'http://localhost:5000/check',
{
fileName: file.name,
fileHash,
},
);
// 上傳未完成的分片
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
if (uploadedChunks?.includes(chunkIndex)) {
console.log('跳過chunkIndx', chunkIndex);
setUploadProgress(((chunkIndex + 1) / totalChunks) * 100); // 更新進(jìn)度
continue; // 跳過已經(jīng)上傳的分片
}
console.log('上傳chunkIndx', chunkIndex);
// 創(chuàng)建當(dāng)前分片
const start = chunkIndex * CHUNK_SIZE; // 分片起始字節(jié)
const end = Math.min(file.size, start + CHUNK_SIZE); // 分片結(jié)束字節(jié)
const chunk = file.slice(start, end); // 獲取分片
// 上傳分片
const formData = new FormData();
formData.append('chunk', chunk); // 當(dāng)前分片
formData.append('fileName', file.name); // 文件名
formData.append('fileHash', fileHash); // 文件唯一標(biāo)識
formData.append('chunkIndex', chunkIndex); // 分片索引
await axios.post(
`http://localhost:5000/upload?fileHash=${fileHash}&chunkIndex=${chunkIndex}&fileName=${file.name}`,
formData,
{
onUploadProgress: (progressEvent) => {
const progress =
((chunkIndex + progressEvent.loaded / progressEvent.total) /
totalChunks) *
100;
setUploadProgress(progress); // 實時更新上傳進(jìn)度
},
},
);
}
// 通知服務(wù)端合并分片
await axios.post('http://localhost:5000/merge', {
fileName: file.name,
fileHash,
totalChunks,
});
alert('上傳成功!');
uploading.current = false; // 標(biāo)記上傳完成
};
return (
<div style={{ padding: '20px' }}>
<p>大文件上傳(支持?jǐn)帱c續(xù)傳)</p>
<input type="file" onChange={handleFileChange} />
<button onClick={handleUpload}>提交上傳文件</button>
<div style={{ marginTop: '20px' }}>
<progress value={uploadProgress} max="100" />
<div>上傳進(jìn)度:{uploadProgress.toFixed(2)}%</div>
</div>
</div>
);
}
后端實現(xiàn)(Node.js)
在后端,您需要安裝以下依賴:multer、fs-extra、express、cors、body-parser??梢允褂靡韵旅睿?/p>
npm i multer fs-extra express cors body-parser
注意: 這些包應(yīng)用于生產(chǎn)環(huán)境和開發(fā)環(huán)境。
以下是Node.js服務(wù)器的實現(xiàn)代碼:
// 文件:server.js
const express = require("express");
const multer = require("multer");
const fs = require("fs");
const bodyParser = require("body-parser");
const path = require("path");
const cors = require("cors");
const app = express();
const uploadDir = path.join(__dirname, "uploads"); // 上傳目錄
// 確保上傳目錄存在
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
app.use(cors()); // 允許跨域請求
app.use(bodyParser.json());
app.use(express.json()); // 解析 JSON 請求體
// 檢查已上傳的分片
app.post("/check", (req, res) => {
const { fileHash } = req.body;
console.log("fileHash check",fileHash)
const fileChunkDir = path.join(uploadDir, fileHash); // 分片存儲目錄
if (!fs.existsSync(fileChunkDir)) {
return res.json([]); // 如果目錄不存在,返回空數(shù)組
}
// 返回已上傳的分片索引
const uploadedChunks = fs.readdirSync(fileChunkDir).map((chunk) => {
return parseInt(chunk.split("-")[1]); // 提取分片索引
});
res.json(uploadedChunks);
});
// 設(shè)置 multer 中間件,用于處理文件上傳
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const fileHash = req.query.fileHash; // 從查詢參數(shù)獲取 fileHash
const chunkDir = path.join(uploadDir, fileHash);
// 確保切片目錄存在
if (!fs.existsSync(chunkDir)) {
fs.mkdirSync(chunkDir, { recursive: true });
}
cb(null, chunkDir);
},
filename: (req, file, cb) => {
const { chunkIndex } = req.query;
cb(null, `chunk-${chunkIndex}`);
},
});
const upload = multer({ storage:storage });
// 上傳文件分片
app.post("/upload", upload.single("chunk"), (req, res) => {
const { fileHash } = req.body;
res.status(200).send("分片上傳成功");
});
// 合并分片
app.post("/merge", (req, res) => {
const { fileName, fileHash, totalChunks } = req.body;
console.log("fileName",req.body)
const fileChunkDir = path.join(uploadDir, fileHash);
const filePath = path.join(uploadDir, fileName);
// 創(chuàng)建可寫流用于最終合并文件
const writeStream = fs.createWriteStream(filePath);
for (let i = 0; i < totalChunks; i++) {
const chunkPath = path.join(fileChunkDir, `chunk-${i}`);
const data = fs.readFileSync(chunkPath); // 讀取分片
writeStream.write(data); // 寫入最終文件
// fs.unlinkSync(chunkPath); // 刪除分片文件--留下來,可以看上傳記錄
}
writeStream.end(); // 關(guān)閉流
// fs.rmdirSync(fileChunkDir); // 刪除分片目錄--留下來,可以看上傳記錄
res.send("文件合并完成");
});
app.listen(5000, () => {
console.log("服務(wù)器已啟動:http://localhost:5000");
});
總結(jié)
通過上述代碼,您可以實現(xiàn)大文件的分片上傳和斷點續(xù)傳功能。這種方法不僅提高了上傳的可靠性,還能有效應(yīng)對網(wǎng)絡(luò)的不穩(wěn)定性。
到此這篇關(guān)于React+Node.js實現(xiàn)大文件傳輸與斷點續(xù)傳的文章就介紹到這了,更多相關(guān)React Node.js大文件傳輸內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react?表單數(shù)據(jù)形式配置化設(shè)計
這篇文章主要介紹了react表單數(shù)據(jù)形式配置化設(shè)計,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-07-07
React Antd Select組件輸入搜索時調(diào)用接口方式
為優(yōu)化Select組件中文輸入時的接口調(diào)用,使用lodash.debounce實現(xiàn)防抖,避免每輸入一個字母即觸發(fā)請求,待用戶點擊空格完成輸入后,再調(diào)用接口獲取數(shù)據(jù),提升性能與用戶體驗2025-08-08
基于React.js實現(xiàn)兔兔牌九宮格翻牌抽獎組件
這篇文章主要為大家詳細(xì)介紹了如何基于React.js實現(xiàn)兔兔牌九宮格翻牌抽獎組件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-01-01
利用React高階組件實現(xiàn)一個面包屑導(dǎo)航的示例
這篇文章主要介紹了利用React高階組件實現(xiàn)一個面包屑導(dǎo)航的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

