vue
vue3+vite自动化注册全局组件
vue3加载远程组件
Vue Vine 编写 Vue 的另一种风格
Vueuse 实现数字滚动 Count-to
学会这几个常用功能,给你的 vue3 项目加点餐!
为什么有些 Vue3 项目已经开始弃用 Pinia 了?
Vue3 的 5 个组合式 API 方法
Vue3+TS+canvas 项目预览图片时,添加水印,浏览器禁止右键功能、前端禁止直接获取图片地址
Vue3.x 生态最能打的组合!
vue2封装常用工具类
使用 v-once 和 v-memo 进行 Vue 渲染优化
基于 Vue + Element plus + Node 实现大文件分片上传,断点续传和秒传的功能!
Vue中的$attrs和$listeners对象有什么作用?
本文档使用 MrDoc 发布
-
+
首页
基于 Vue + Element plus + Node 实现大文件分片上传,断点续传和秒传的功能!
## 项目初始化 首先,我们需要初始化一个 Vue 项目。如果你还没有安装 Vue CLI,可以通过以下命令安装: ```bash npm install -g @vue/cli ``` 然后,创建一个新的 Vue 项目: ```bash vue create file-upload-demo cd file-upload-demo ``` 选择默认配置或者根据自己的需求进行配置。创建完成后,进入项目目录并启动开发服务器: ```bash npm run serve ``` ## 安装和配置 Element Plus 为了使用 Element Plus,我们需要先安装它: ```bash npm install element-plus --save ``` 在 `src/main.js` 中引入 Element Plus: ```js import { createApp } from 'vue'; import App from './App.vue'; import ElementPlus from 'element-plus'; import 'element-plus/lib/theme-chalk/index.css'; const app = createApp(App); app.use(ElementPlus); app.mount('#app'); ``` ## 实现分片上传 ### 前端代码 首先,我们需要在前端实现文件分片上传的逻辑。在 `src/components` 目录下创建一个 `FileUpload.vue` 文件,并添加以下内容: ```vue <template> <el-upload class="upload-demo" ref="upload" :http-request="uploadFile" :on-change="handleChange" :auto-upload="false" :before-upload="beforeUpload" :multiple="false"> <el-button slot="trigger" type="primary">选取文件</el-button> <el-button @click="submitUpload">上传</el-button> </el-upload> </template> <script> export default { data() { return { file: null, chunkSize: 2 * 1024 * 1024 // 2MB }; }, methods: { handleChange(file) { this.file = file.raw; }, beforeUpload(file) { this.file = file; return false; }, async uploadFile() { const chunkCount = Math.ceil(this.file.size / this.chunkSize); for (let i = 0; i < chunkCount; i++) { const chunk = this.file.slice(i * this.chunkSize, (i + 1) * this.chunkSize); const formData = new FormData(); formData.append('chunk', chunk); formData.append('index', i); formData.append('fileName', this.file.name); await this.uploadChunk(formData); } }, async uploadChunk(formData) { try { const response = await fetch('http://localhost:3000/upload', { method: 'POST', body: formData }); const result = await response.json(); console.log(result); } catch (error) { console.error('上传失败:', error); } }, submitUpload() { this.uploadFile(); } } }; </script> <style scoped> .upload-demo { display: flex; flex-direction: column; } </style> ``` ### 后端代码 在后端,我们需要处理分片上传的逻辑。以下是一个使用 Node.js 和 Express 实现的示例: ```js const express = require('express'); const multer = require('multer'); const fs = require('fs'); const path = require('path'); const app = express(); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.single('chunk'), (req, res) => { const { index, fileName } = req.body; const chunkFilePath = path.join(__dirname, 'uploads', `${fileName}-${index}`); fs.renameSync(req.file.path, chunkFilePath); res.json({ message: '上传成功', index }); }); app.listen(3000, () => { console.log('Server started on http://localhost:3000'); }); ``` ## 实现断点续传 ### 前端代码 为了实现断点续传,我们需要记录已经上传的分片,并在网络中断后继续上传未完成的分片。 ```vue <template> <el-upload class="upload-demo" ref="upload" :http-request="uploadFile" :on-change="handleChange" :auto-upload="false" :before-upload="beforeUpload" :multiple="false"> <el-button slot="trigger" type="primary">选取文件</el-button> <el-button @click="submitUpload">上传</el-button> </el-upload> </template> <script> export default { data() { return { file: null, chunkSize: 2 * 1024 * 1024, // 2MB uploadedChunks: [] }; }, methods: { handleChange(file) { this.file = file.raw; }, beforeUpload(file) { this.file = file; return false; }, async uploadFile() { const chunkCount = Math.ceil(this.file.size / this.chunkSize); const response = await fetch(`http://localhost:3000/uploaded-chunks?fileName=${this.file.name}`); this.uploadedChunks = await response.json(); for (let i = 0; i < chunkCount; i++) { if (this.uploadedChunks.includes(i)) continue; const chunk = this.file.slice(i * this.chunkSize, (i + 1) * this.chunkSize); const formData = new FormData(); formData.append('chunk', chunk); formData.append('index', i); formData.append('fileName', this.file.name); await this.uploadChunk(formData); } }, async uploadChunk(formData) { try { const response = await fetch('http://localhost:3000/upload', { method: 'POST', body: formData }); const result = await response.json(); this.uploadedChunks.push(result.index); console.log(result); } catch (error) { console.error('上传失败:', error); } }, submitUpload() { this.uploadFile(); } } }; </script> <style scoped> .upload-demo { display: flex; flex-direction: column; } </style> ``` ### 后端代码 后端需要记录已上传的分片,并在客户端请求时返回这些信息。 ```js const express = require('express'); const multer = require('multer'); const fs = require('fs'); const path = require('path'); const app = express(); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.single('chunk'), (req, res) => { const { index, fileName } = req.body; const chunkFilePath = path.join(__dirname, 'uploads', `${fileName}-${index}`); fs.renameSync(req.file.path, chunkFilePath); res.json({ message: '上传成功', index }); }); app.get('/uploaded-chunks', (req, res) => { const { fileName } = req.query; const uploadedChunks = []; fs.readdirSync(path.join(__dirname, 'uploads')).forEach(file => { const match = file.match(new RegExp(`${fileName}-(\\d+)`)); if (match) { uploadedChunks.push(Number(match[1])); } }); res.json(uploadedChunks); }); app.listen(3000, () => { console.log('Server started on http://localhost:3000'); }); ``` ## 实现秒传功能 ### 前端代码 秒传功能依赖于文件的哈希值。在上传前,我们先计算文件的哈希值,并检查服务器是否已经存在相同的文件。 ```vue <template> <el-upload class="upload-demo" ref="upload" :http-request="uploadFile" :on-change="handleChange" :auto-upload="false" :before-upload="beforeUpload" :multiple="false"> <el-button slot="trigger" type="primary">选取文件</el-button> <el-button @click="submitUpload">上传</el-button> </el -upload> </template> <script> import SparkMD5 from 'spark-md5'; export default { data() { return { file: null, chunkSize: 2 * 1024 * 1024, // 2MB uploadedChunks: [], fileHash: '' }; }, methods: { handleChange(file) { this.file = file.raw; }, beforeUpload(file) { this.file = file; this.calculateHash(file); return false; }, calculateHash(file) { const chunkSize = this.chunkSize; const chunks = Math.ceil(file.size / chunkSize); const spark = new SparkMD5.ArrayBuffer(); let currentChunk = 0; const fileReader = new FileReader(); fileReader.onload = e => { spark.append(e.target.result); currentChunk++; if (currentChunk < chunks) { loadNext(); } else { this.fileHash = spark.end(); console.log('文件哈希值:', this.fileHash); } }; const loadNext = () => { const start = currentChunk * chunkSize; const end = Math.min(start + chunkSize, file.size); fileReader.readAsArrayBuffer(file.slice(start, end)); }; loadNext(); }, async uploadFile() { const response = await fetch(`http://localhost:3000/check-file?hash=${this.fileHash}`); const { exists } = await response.json(); if (exists) { console.log('文件已存在,秒传成功'); return; } const chunkCount = Math.ceil(this.file.size / this.chunkSize); const uploadedChunksResponse = await fetch(`http://localhost:3000/uploaded-chunks?fileName=${this.file.name}`); this.uploadedChunks = await uploadedChunksResponse.json(); for (let i = 0; i < chunkCount; i++) { if (this.uploadedChunks.includes(i)) continue; const chunk = this.file.slice(i * this.chunkSize, (i + 1) * this.chunkSize); const formData = new FormData(); formData.append('chunk', chunk); formData.append('index', i); formData.append('fileName', this.file.name); formData.append('hash', this.fileHash); await this.uploadChunk(formData); } }, async uploadChunk(formData) { try { const response = await fetch('http://localhost:3000/upload', { method: 'POST', body: formData }); const result = await response.json(); this.uploadedChunks.push(result.index); console.log(result); } catch (error) { console.error('上传失败:', error); } }, submitUpload() { this.uploadFile(); } } }; </script> <style scoped> .upload-demo { display: flex; flex-direction: column; } </style> ``` ### 后端代码 后端需要支持文件哈希检查和已存在文件的处理。 ```js const express = require('express'); const multer = require('multer'); const fs = require('fs'); const path = require('path'); const app = express(); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.single('chunk'), (req, res) => { const { index, fileName, hash } = req.body; const chunkFilePath = path.join(__dirname, 'uploads', `${fileName}-${index}`); fs.renameSync(req.file.path, chunkFilePath); // 合并文件 const chunkCount = Math.ceil(req.file.size / (2 * 1024 * 1024)); const chunks = []; for (let i = 0; i < chunkCount; i++) { chunks.push(fs.readFileSync(path.join(__dirname, 'uploads', `${fileName}-${i}`))); } fs.writeFileSync(path.join(__dirname, 'uploads', fileName), Buffer.concat(chunks)); res.json({ message: '上传成功', index }); }); app.get('/uploaded-chunks', (req, res) => { const { fileName } = req.query; const uploadedChunks = []; fs.readdirSync(path.join(__dirname, 'uploads')).forEach(file => { const match = file.match(new RegExp(`${fileName}-(\\d+)`)); if (match) { uploadedChunks.push(Number(match[1])); } }); res.json(uploadedChunks); }); app.get('/check-file', (req, res) => { const { hash } = req.query; const filePath = path.join(__dirname, 'uploads', hash); const exists = fs.existsSync(filePath); res.json({ exists }); }); app.listen(3000, () => { console.log('Server started on http://localhost:3000'); }); ``` ## 总结 希望通过本文的介绍,大家能够更深入地了解大文件上传的实现方法,并在实际项目中灵活应用这些技巧,提升用户体验。
admin
2024年7月2日 11:51
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码