React+Node.js實(shí)現(xiàn)大文件傳輸與斷點(diǎn)續(xù)傳
簡(jiǎn)述
使用React前端和Node.js后端實(shí)現(xiàn)大文件傳輸和斷點(diǎn)續(xù)傳的功能。通過分片上傳技術(shù),可以有效地解決網(wǎng)絡(luò)不穩(wěn)定帶來的傳輸中斷問題。
前端實(shí)現(xiàn)(React)
首先,您需要在前端項(xiàng)目中安裝axios
、spark-md5
庫以處理HTTP請(qǐng)求??梢允褂靡韵旅睿?/p>
npm i axios spark-md5
以下是實(shí)現(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); // 用于存儲(chǔ)用戶選擇的文件 const [uploadProgress, setUploadProgress] = useState(0); // 上傳進(jìn)度 const uploading = useRef(false); // 用于防止重復(fù)觸發(fā)上傳邏輯 // 當(dāng)用戶選擇文件時(shí)觸發(fā) const handleFileChange = (e) => { setFile(e.target.files[0]); // 將選擇的文件存入狀態(tài) setUploadProgress(0); // 重置上傳進(jìn)度 }; // 計(jì)算文件的唯一標(biāo)識(shí) (哈希) 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)識(shí)(哈希值) console.log('fileHash', fileHash); const totalChunks = Math.ceil(file.size / CHUNK_SIZE); // 計(jì)算文件分片總數(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)識(shí) 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); // 實(shí)時(shí)更新上傳進(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(diǎn)續(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> ); }
后端實(shí)現(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ù)器的實(shí)現(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()); // 允許跨域請(qǐng)求 app.use(bodyParser.json()); app.use(express.json()); // 解析 JSON 請(qǐng)求體 // 檢查已上傳的分片 app.post("/check", (req, res) => { const { fileHash } = req.body; console.log("fileHash check",fileHash) const fileChunkDir = path.join(uploadDir, fileHash); // 分片存儲(chǔ)目錄 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ù)器已啟動(dòng):http://localhost:5000"); });
總結(jié)
通過上述代碼,您可以實(shí)現(xiàn)大文件的分片上傳和斷點(diǎn)續(xù)傳功能。這種方法不僅提高了上傳的可靠性,還能有效應(yīng)對(duì)網(wǎng)絡(luò)的不穩(wěn)定性。
到此這篇關(guān)于React+Node.js實(shí)現(xiàn)大文件傳輸與斷點(diǎn)續(xù)傳的文章就介紹到這了,更多相關(guān)React Node.js大文件傳輸內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- React + Node.js實(shí)現(xiàn)圖片上傳功能
- React和Node.js快速上傳進(jìn)度條功能實(shí)現(xiàn)
- Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解
- React+Node實(shí)現(xiàn)大文件分片上傳、斷點(diǎn)續(xù)傳秒傳思路
- nodejs和react實(shí)現(xiàn)即時(shí)通訊簡(jiǎn)易聊天室功能
- React+react-dropzone+node.js實(shí)現(xiàn)圖片上傳的示例代碼
- 基于Node的React圖片上傳組件實(shí)現(xiàn)實(shí)例代碼
相關(guān)文章
簡(jiǎn)單的React SSR服務(wù)器渲染實(shí)現(xiàn)
這篇文章主要介紹了簡(jiǎn)單的React SSR服務(wù)器渲染實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12React實(shí)現(xiàn)全局組件的Toast輕提示效果
這篇文章主要介紹了React實(shí)現(xiàn)全局組件的Toast輕提示效果,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09react如何利用useRef、forwardRef、useImperativeHandle獲取并處理dom
這篇文章主要介紹了react如何利用useRef、forwardRef、useImperativeHandle獲取并處理dom,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10react中history(push,go,replace)切換路由方法的區(qū)別及說明
這篇文章主要介紹了react中history(push,go,replace)切換路由方法的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10react使用antd-design中select不能及時(shí)刷新問題及解決
這篇文章主要介紹了react使用antd-design中select不能及時(shí)刷新問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook
這篇文章主要為大家介紹了useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07react裝飾器與高階組件及簡(jiǎn)單樣式修改的操作詳解
這篇文章主要介紹了react裝飾器與高階組件及簡(jiǎn)單樣式修改的操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-09-09