当前位置: 首页 > news >正文

模版之家seo交流

模版之家,seo交流,装饰工程公司,个人怎么制作网站目录 1.大文件上传的场景 2.前端实现 2.1 对文件进行分片 2.2 生成hash值(唯一标识) 2.3 发送上传文件请求 3.后端实现 3.1 接收分片数据临时存储 3.2 合并分片 4.完成段点续传 4.1修改后端 4.2 修改前端 5.测试 博客主页:専心_前端…

 

目录

1.大文件上传的场景

2.前端实现

2.1 对文件进行分片

 2.2 生成hash值(唯一标识)

2.3 发送上传文件请求

3.后端实现

 3.1 接收分片数据临时存储

3.2 合并分片

4.完成段点续传

4.1修改后端

4.2 修改前端

5.测试


博客主页:専心_前端,javascript,mysql-CSDN博客

 系列专栏:vue3+nodejs 实战--文件上传

 前端代码仓库:jiangjunjie666/my-upload: vue3+nodejs 上传文件的项目,用于学习 (github.com)

 后端代码仓库:jiangjunjie666/my-upload-server: nodejs上传文件的后端 (github.com)

 欢迎关注

 在上一篇中我们实现了文件的拖拽上传,Vue3 + Nodejs 实战 ,文件上传项目--实现拖拽上传-CSDN博客

 该篇就讲讲大文件上传的需求场景

1.大文件上传的场景

如果遇到需要上传电影或者视频之类的需求,那么上传的文件是非常大的,这个时候我们不能说用一个请求就直接将所有的文件传输过去,因为这个大文件上传时间相比较来说是比较长的,存在很多的弊端,假如用户刷新了页面之类的情况,这时候上传又需要重头开始上传,这对用户以及服务器都是不妥的。

需要解决的基本问题(其他的扩展根据需求来定)

  • 对大文件进行分片上传
  • 上传的实时进度显示
  • 上传中断后再次上传跳过已经上传的部分

2.前端实现

2.1 对文件进行分片

对文件进行分片我们用到的是  file.slice(i,j) 这个方法

其中的参数代表的意思就是切割的大小,以size单位的,i-j 就是切这一段的数据

<template><input type="file" @change="fileChange" />
</template><scritp>
import { ref } from 'vue'
const fileList = ref([])
const file = ref([])
const fileChange = (e) => {console.log(e.target.files[0])//分片file.value = e.target.files[0]for (let i = 0; i < file.value.size; i += 1024 * 1024) {fileList.value.push(file.value.slice(i, i + 1024 * 1024))}
}
</script>

这样就将文件切成了每个大小为 1024 * 1024 的大小的文件,也就是1mb

 2.2 生成hash值(唯一标识)

因为我们进行分片后的文件数据并不能单独成为一个原本的文件,一般都是二进制数据,我们在分片上传的时候需要临时存储已经上传的文件,这时候为了能对应到每个文件,需要对每个文件生成对应的文件hash值,这里需要用到hash算法,项目中大家可以直接使用spark-md5这个库生成文件hash。

总之文件hash的作用就是生成唯一的标识,该算法生成的hash值永远不会出现重复 ,但是同一文件多次生成的都是一样的。

安装 spark-md5

npm i spark-md5

引入生成hash

 import SparkMD5 from 'spark-md5'const fileMd5 = ref('')const hash = new SparkMD5.ArrayBuffer() // 构建hash值对象const fileReader = new FileReader()fileReader.onload = () => {hash.append(fileReader.result)fileMd5.value = hash.end()}fileReader.readAsArrayBuffer(file.value)

2.3 发送上传文件请求

大文件分片上传一般都是二个请求,一个是分片上传的请求,一个是分片上传完之后的合并请求。

分片上传:

const upload = async (index) => {if (index == fileList.value.length) {mergeUpload()return}const formData = new FormData()formData.append('chunk', fileList.value[index])formData.append('index', index)formData.append('name', fileMd5.value + '@' + index) // 临时的二进制文件分片formData.append('filename', fileMd5.value) // 文件名,采用hashlet res = await http.post('/api/upload_chunk1', formData)if(res.code == 200){upload(index+1)}else{upload(index) // 失败重新上传    }
}

合并分片:

const mergeUpload = async () => {//合并请求let res = await http.post('/api/merge_chunk', {filename: fileMd5.value, //最后合并的文件名extname: file.value.type.split('/').pop() //文件后缀})if ((res.code = 200)) {file.value = nullfileList.value = []fileMd5.value = ''ElMessage({type: 'success',message: '上传成功'})}
}

3.后端实现

我这边使用的是 multiparty 中间件完成的该需求,可以安装一下

npm i multiparty

如果没有看过建的项目,可以看看第一期喔:Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传-CSDN博客

 3.1 接收分片数据临时存储

首先要做的就是利用请求中携带的数据进行创建一个临时保存分片数据的目录,目录名可以使用hash值,这样可以做到唯一性,并且后面实现断点续传也能更方便。

分片上传的数据会被临时保存在 Temp 文件夹中,如果windows电脑的话,可以 win + R ,输入 %temp% 就能进入该目录,里面保存的都是临时文件。我的做法就是将这个的文件copy一份到对应的目录下,然后删除临时文件。

const multiparty = require('multiparty')
const path = require('path')
const fs = require('fs')
exports.upload_chunk1 = (req, res, next) => {// 二进制数据上传const form = new multiparty.Form()form.parse(req, (err, fields, files) => {if (err) {next(err)return}//将每一次上传的数据进行统一的存储const oldName = files.chunk[0].pathconst newName = path.join(__dirname, '../../public/upload/chunk/' + fields['filename'][0] + '/' + fields['name'][0])//创建临时存储目录fs.mkdirSync('./public/upload/chunk/' + fields['filename'][0], {recursive: true})console.log(fields)console.log(files)fs.copyFile(oldName, newName, (err) => {if (err) {console.error(err)} else {// 删除源文件fs.unlink(oldName, (err) => {if (err) {console.error(err)} else {console.log('文件复制和删除成功')}})}})res.send({code: 200,msg: '分片上传成功'})})
}

这是刚刚上传的一批二进制文件

3.2 合并分片

合并接口:

exports.merge_chunk = (req, res, next) => {const fields = req.bodyconsole.log(fields)thunkStreamMerge('../../public/upload/chunk/' + fields.filename, '../../public/upload/' + fields.filename + '.' + fields.extname)res.send({code: 200,data: '/public/upload/' + fields.filename + '.' + fields.extname})
}

编写一个thunkStreamMerge 函数用来合并分块文件。它接收两参数:源文件目录 sourceFiles 和目标文件路径 targetFile。利用 fs.readdirSync 读取源文件目录下的文件列表,并根据文件名中的序号排序,以确保按正确的顺序合并文件。接着创建一个可写流 (fileWriteStream),用于将所有分块文件的内容写入到目标文件中。这个目标文件路径是由 targetFile 指定的。

// 文件合并
function thunkStreamMerge(sourceFiles, targetFile) {const chunkFilesDir = path.join(__dirname, sourceFiles)const chunkTargetDir = path.join(__dirname, targetFile)const list = fs.readdirSync(chunkFilesDir) //读取目录中的文件const fileList = list.sort((a, b) => a.split('@')[1] * 1 - b.split('@')[1] * 1).map((name) => ({name,filePath: path.resolve(chunkFilesDir, name)}))const fileWriteStream = fs.createWriteStream(chunkTargetDir)thunkStreamMergeProgress(fileList, fileWriteStream, chunkFilesDir)
}

thunkStreamMergeProgress 函数是用来处理每个分块文件的函数。它接收三个参数:fileList,包含了分块文件信息的数组;fileWriteStream,目标文件的可写流;以及可选的 sourceFiles,用于删除临时文件目录。

首先,函数检查 fileList 是否为空,如果为空,表示所有分块文件已经合并完成。此时,它向目标文件写入一个标识('完成了'),并根据需要删除源文件目录 sourceFiles

如果 fileList 不为空,函数会从中取出第一个文件信息,并获取其文件路径。然后,它创建一个可读流 (currentReadStream) 来读取该分块文件的内容。

接下来,函数使用 .pipe 方法将当前可读流的内容传输到目标文件的可写流中,这将逐步将分块文件的内容写入目标文件。

当当前可读流读取完毕(end 事件触发)后,它递归调用 thunkStreamMergeProgress 函数,处理下一个分块文件,直到所有分块文件都合并到了目标文件中。

//合并每一个分片
function thunkStreamMergeProgress(fileList, fileWriteStream, sourceFiles) {if (!fileList.length) {// thunkStreamMergeProgress(fileList)fileWriteStream.end('完成了')// 删除临时目录if (sourceFiles) fs.rmdirSync(sourceFiles, { recursive: true, force: true })return}const data = fileList.shift() // 取第一个数据const { filePath: chunkFilePath } = dataconst currentReadStream = fs.createReadStream(chunkFilePath) // 读取文件// 把结果往最终的生成文件上进行拼接currentReadStream.pipe(fileWriteStream, { end: false })currentReadStream.on('end', () => {// console.log(chunkFilePath);// 拼接完之后进入下一次循环thunkStreamMergeProgress(fileList, fileWriteStream, sourceFiles)})
}

这样做完后就能实现基本的文件分片上传以及合并分片了

这是上传的一个视频

4.完成段点续传

4.1修改后端

 断点续传的情况就是文件上传至一半时突然做了其他的交互让上传停止了,那么下次再上传时就需要重新开始从头上传,这样非常消耗时间,对用户的体验与服务器的维护都不友好,这时候就要判断是否有对应的文件数据,直接将对应的索引传给客户端,客户端直接从该分片开始传输即可,因为我们之前给每个临时文件都加了 hash+@+index,我们可以读取文件夹目录查找最后的index传输给客户端即可。

当然这种解决方法是因为我没有采用数据库的情况,如果采用数据库或者其他的需求,基本的实现思路都差不多,利用唯一值 hash 进行传输。

后端接口加上一个判断,是否为段点传输

exports.upload_chunk1 = (req, res, next) => {// 二进制数据上传const form = new multiparty.Form()form.parse(req, (err, fields, files) => {if (err) {next(err)return}let pa = path.join(__dirname, '../../public/upload/chunk/' + fields['filename'][0])console.log(pa)//判断是否为断点续传if (fs.existsSync(pa) && parseInt(fields.index[0]) === 0) {//存在该目录//返回最大的索引let maxIndex = 0let arr = fs.readdirSync(pa)for (let i = 0; i < arr.length; i++) {let str = parseInt(arr[i].split('@')[1])console.log(str)if (str > maxIndex) {maxIndex = str}}res.send({code: 300,msg: '存在该目录,请继续上传',index: maxIndex})} else {//将每一次上传的数据进行统一的存储const oldName = files.chunk[0].pathconst newName = path.join(__dirname, '../../public/upload/chunk/' + fields['filename'][0] + '/' + fields['name'][0])//创建临时存储目录fs.mkdirSync('./public/upload/chunk/' + fields['filename'][0], {recursive: true})console.log(fields)console.log(files)fs.copyFile(oldName, newName, (err) => {if (err) {console.error(err)} else {// 删除源文件fs.unlink(oldName, (err) => {if (err) {console.error(err)} else {console.log('文件复制和删除成功')}})}})res.send({code: 200,msg: '分片上传成功'})}})
}

4.2 修改前端

前端加上进度条,发送请求时对相应的数据进行判断即可完成此需求了

分片请求,合并请求不变

const upload = async (index) => {if (index == fileList.value.length) {mergeUpload()return}const formData = new FormData()formData.append('chunk', fileList.value[index])formData.append('index', index)formData.append('name', fileMd5.value + '@' + index) // 名字formData.append('filename', fileMd5.value) // 文件名let res = await http.post('/api/upload_chunk1', formData)console.log(res)if (res.code == 300) {//证明已经存在部分文件percentage.value = ((res.index / fileList.value.length) * 100).toFixed(2)upload(res.index + 1)} else if (res.code == 200) {percentage.value = (((index + 1) / fileList.value.length) * 100).toFixed(2)upload(index + 1)} else {upload(index)}
}

加上进度条

<template><div><input type="file" @change="fileChange" /><div class="progress"><el-progress :text-inside="true" :stroke-width="24" :percentage="percentage" status="success" /></div></div>
</template><script>let percentage = ref(0)</script>

5.测试

我这里选择一个 624Mb的视频

我在上传进度到一半时刷新了页面

 分片文件也只有450个

这时候重新选择该视频重新上传

这时候进度条很快就能到对应的进度

后端也没有上传多余的文件,直接接着传输,速度很快就传输完并且合并成了视频

到这基本的分片传输 + 断点续传就实现了,可能还存在者一些小问题,这个大家再项目中根据需求来做相应的改变。

具体的详细代码请大家到仓库下载,或者可以去我的主页资源中下载源码。


文章转载自:
http://cpu.c7500.cn
http://fibrosarcoma.c7500.cn
http://drinkie.c7500.cn
http://bun.c7500.cn
http://paperback.c7500.cn
http://proseminar.c7500.cn
http://pretty.c7500.cn
http://bailer.c7500.cn
http://earthfall.c7500.cn
http://interacinous.c7500.cn
http://positif.c7500.cn
http://dustup.c7500.cn
http://beanpole.c7500.cn
http://rfz.c7500.cn
http://ascii.c7500.cn
http://orthopterology.c7500.cn
http://dichroic.c7500.cn
http://quickie.c7500.cn
http://homonymous.c7500.cn
http://transition.c7500.cn
http://turnaround.c7500.cn
http://mareograph.c7500.cn
http://gewgawish.c7500.cn
http://fencible.c7500.cn
http://autodial.c7500.cn
http://moneyed.c7500.cn
http://aureus.c7500.cn
http://tearproof.c7500.cn
http://sep.c7500.cn
http://unshaken.c7500.cn
http://kinetonucleus.c7500.cn
http://immurement.c7500.cn
http://detruncate.c7500.cn
http://ensky.c7500.cn
http://clairaudient.c7500.cn
http://polystyle.c7500.cn
http://rousing.c7500.cn
http://wayfare.c7500.cn
http://feldberg.c7500.cn
http://teutonization.c7500.cn
http://rivalless.c7500.cn
http://jaunce.c7500.cn
http://agrostography.c7500.cn
http://rapscallion.c7500.cn
http://electron.c7500.cn
http://bulldike.c7500.cn
http://illocution.c7500.cn
http://quarrier.c7500.cn
http://allatectomy.c7500.cn
http://forestry.c7500.cn
http://vauntful.c7500.cn
http://enterology.c7500.cn
http://weediness.c7500.cn
http://outside.c7500.cn
http://censer.c7500.cn
http://agreement.c7500.cn
http://continentalize.c7500.cn
http://hypophosphate.c7500.cn
http://bourdon.c7500.cn
http://amenorrhea.c7500.cn
http://consignable.c7500.cn
http://dormitive.c7500.cn
http://antiwhite.c7500.cn
http://intransit.c7500.cn
http://mannose.c7500.cn
http://courtroom.c7500.cn
http://fruitery.c7500.cn
http://tenebrescence.c7500.cn
http://acrodynia.c7500.cn
http://hydroponist.c7500.cn
http://notch.c7500.cn
http://wildcat.c7500.cn
http://gangsa.c7500.cn
http://chenab.c7500.cn
http://epiphany.c7500.cn
http://deoxidization.c7500.cn
http://interbedded.c7500.cn
http://dumpage.c7500.cn
http://loud.c7500.cn
http://superbity.c7500.cn
http://cylindrite.c7500.cn
http://elbowboard.c7500.cn
http://insinuating.c7500.cn
http://ridable.c7500.cn
http://decomposition.c7500.cn
http://compressor.c7500.cn
http://odra.c7500.cn
http://hellene.c7500.cn
http://congested.c7500.cn
http://iconolater.c7500.cn
http://corkboard.c7500.cn
http://reticulocyte.c7500.cn
http://hepatotoxin.c7500.cn
http://greensboro.c7500.cn
http://cleavable.c7500.cn
http://inofficial.c7500.cn
http://pteropod.c7500.cn
http://capsid.c7500.cn
http://padishah.c7500.cn
http://colaborer.c7500.cn
http://www.zhongyajixie.com/news/90253.html

相关文章:

  • 网站分享按钮关键词排名优化易下拉软件
  • 泰州网站建设营销方法有哪些
  • 建设部网站1667号下载网红推广
  • 网站做任务好看的web网页
  • 网站建设 管理seo外包公司哪家专业
  • 昆山普立斯特做的有网站万网商标查询
  • bp链接生成器网站深圳网站公司排名
  • 如何做线上推广南京seo网站管理
  • 江苏水利工程建设局网站百度搜索广告价格
  • 系统网站界面设计免费优化推广网站的软件
  • 北京诚通新新建设有限公司网站外贸建站与推广
  • 苏州招聘网站开发网站优化推广的方法
  • 公众号开通小程序东莞关键词seo优化
  • wordpress星评分青岛seo关键词优化排名
  • 和狗做的网站三只松鼠网络营销策略
  • wordpress自定义右键seo去哪学
  • 网站中的实名身份证验证怎么做抖音十大搜索关键词
  • wordpress域名无法访问兰州网站seo优化
  • 长沙网络推广哪家好点网站排名优化系统
  • 手机网站底部电话crm
  • 贵阳个人做网站最近一周的时政热点新闻
  • 怎让做淘宝网站广告优化师怎么学
  • 怎么做网站登录站谷歌seo服务
  • 做网站被网监叫去很多次整合营销的案例
  • 室内设计效果图 装修广州优化营商环境条例
  • 有没有做牛羊角的网站成功的网络营销案例ppt
  • 做徽商要做网站吗汽车网络营销的方式有哪些
  • 大连免费建站模板百度网盘手机版
  • 房产网手机版网站建设目标广东的seo产品推广服务公司
  • 日照网站建设哪家好seo研究协会