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

横岗做网站公司最专业的佛山网站建设价格

横岗做网站公司,最专业的佛山网站建设价格,好视通视频会议app下载安装,建设申请网站前言 前面记录过使用库实现的大文件的分片上传 基于WebUploader实现大文件分片上传 基于vue-simple-uploader 实现大文件分片上传 前面记录过基于库实现的大文件的分片上传#xff0c;那如果不使用库#xff0c; 文件分片是怎么实现的#xff0c;该怎么做到呢#xff1f;…前言 前面记录过使用库实现的大文件的分片上传 基于WebUploader实现大文件分片上传 基于vue-simple-uploader 实现大文件分片上传 前面记录过基于库实现的大文件的分片上传那如果不使用库 文件分片是怎么实现的该怎么做到呢 一起看看吧思路 1、文件分片、 2、每个文件标识、 3、并发上传、 4、合并组装 5、上传前查询是否存在实现 读取文件 通过监听 input 的 change 事件当选取了本地文件后可以在回调函数中拿到对应的文件 const handleUpload (e: Event) {const files (e.target as HTMLInputElement).filesif (!files) {return}// 读取选择的文件console.log(files[0]); }文件分片 核心是用Blob 对象的 slice 方法,用法如下 let blob instanceOfBlob.slice([start [, end [, contentType]]]}; start 和 end 代表 Blob 里的下标表示被拷贝进新的 Blob 的字节的起始位置和结束位置。 contentType 会给新的 Blob 赋予一个新的文档类型在这里我们用不到。使用slice方法来实现下对文件的分片,获取分片的文件列表 const createFileChunks (file: File) {const fileChunkList []let cur 0while (cur file.size) {fileChunkList.push({file: file.slice(cur, cur CHUNK_SIZE),})cur CHUNK_SIZE // CHUNK_SIZE为分片的大小}return fileChunkList }hash 计算 怎么区分每一个文件呢 1、根据文件名去区分不可以因为文件名我们可以是随便修改的 2、我们见过用 webpack 打包出来的文件的文件名会有一串不一样的字符串这个字符串就是根据文件的内容生成的 hash 值文件内容变化hash 值就会跟着发生变化。 3、而且妙传实现也是基于此 服务器在处理上传文件的请求的时候要先判断下对应文件的 hash 值有没有记录如果 A 和 B 先后上传一份内容相同的文件 所以这两份文件的 hash 值是一样的。当 A 上传的时候会根据文件内容生成一个对应的 hash 值然后在服务器上就会有一个对应的文件B 再上传的时候服务器就会发现这个文件的 hash 值之前已经有记录了说明之前 已经上传过相同内容的文件了所以就不用处理 B 的这个上传请求了给用户的感觉就像是实现了秒传spark-md5 我们得先安装spark-md5。我们就可以用文件的所有切片来算该文件的hash 值 但是如果一个文件特别大每个切片的所有内容都参与计算的话会很耗时间所有我们可以采取以下策略 1、第一个和最后一个切片的内容全部参与计算 2、中间剩余的切片我们分别在前面、后面和中间取 2 个字节参与计算 3、既能保证所有的切片参与了计算也能保证不耗费很长的时间安装使用 npm install spark-md5 npm install types/spark-md5 -Dimport SparkMD5 from spark-md5/*** 计算文件的hash值计算的时候并不是根据所用的切片的内容去计算的那样会很耗时间我们采取下面的策略去计算* 1. 第一个和最后一个切片的内容全部参与计算* 2. 中间剩余的切片我们分别在前面、后面和中间取2个字节参与计算* 这样做会节省计算hash的时间*/ const calculateHash async (fileChunks: Array{file: Blob}) {return new Promise(resolve {const spark new sparkMD5.ArrayBuffer()const chunks: Blob[] []fileChunks.forEach((chunk, index) {if (index 0 || index fileChunks.length - 1) {// 1. 第一个和最后一个切片的内容全部参与计算chunks.push(chunk.file)} else {// 2. 中间剩余的切片我们分别在前面、后面和中间取2个字节参与计算// 前面的2字节chunks.push(chunk.file.slice(0, 2))// 中间的2字节chunks.push(chunk.file.slice(CHUNK_SIZE / 2, CHUNK_SIZE / 2 2))// 后面的2字节chunks.push(chunk.file.slice(CHUNK_SIZE - 2, CHUNK_SIZE))}})const reader new FileReader()reader.readAsArrayBuffer(new Blob(chunks))reader.onload (e: Event) {spark.append(e?.target?.result as ArrayBuffer)resolve(spark.end())}}) }文件上传前端实现 const uploadChunks async (fileChunks: Array{ file: Blob }) {const data fileChunks.map(({ file }, index) ({fileHash: fileHash.value,index,chunkHash: ${fileHash.value}-${index},chunk: file,size: file.size,}))const formDatas data.map(({ chunk, chunkHash }) {const formData new FormData()// 切片文件formData.append(chunk, chunk)// 切片文件hashformData.append(chunkHash, chunkHash)// 大文件的文件名formData.append(fileName, fileName.value)// 大文件hashformData.append(fileHash, fileHash.value)return formData})let index 0const max 6 // 并发请求数量const taskPool: any [] // 请求队列while (index formDatas.length) {const task fetch(http://127.0.0.1:3000/upload, {method: POST,body: formDatas[index],})task.then(() {taskPool.splice(taskPool.findIndex((item: any) item task))})taskPool.push(task)if (taskPool.length max) {// 当请求队列中的请求数达到最大并行请求数的时候得等之前的请求完成再循环下一个await Promise.race(taskPool)}indexpercentage.value ((index / formDatas.length) * 100).toFixed(0)}await Promise.all(taskPool) }文件上传后端实现 后端 express 框架用到的工具包multiparty、fs-extra、cors、body-parser、nodemon后端我们处理文件时需要用到 multiparty 这个工具所以也是得先安装然后再引入它。 我们在处理每个上传的分片的时候应该先将它们临时存放到服务器的一个地方方便我们合并的时候再去读 取。为了区分不同文件的分片我们就用文件对应的那个 hash 为文件夹的名称将这个文件的所有分片放到这 个文件夹中。// 所有上传的文件存放到该目录下 const UPLOAD_DIR path.resolve(__dirname, uploads)// 处理上传的分片 app.post(/upload, async (req, res) {const form new multiparty.Form()form.parse(req, async function (err, fields, files) {if (err) {res.status(401).json({ok: false,msg: 上传失败,})}const chunkHash fields[chunkHash][0]const fileName fields[fileName][0]const fileHash fields[fileHash][0]// 存储切片的临时文件夹const chunkDir path.resolve(UPLOAD_DIR, fileHash)// 切片目录不存在则创建切片目录if (!fse.existsSync(chunkDir)) {await fse.mkdirs(chunkDir)}const oldPath files.chunk[0].path// 把文件切片移动到我们的切片文件夹中await fse.move(oldPath, path.resolve(chunkDir, chunkHash))res.status(200).json({ok: true,msg: received file chunk,})}) })写完前后端代码后就可以来试下看看文件能不能实现切片的上传如果没有错误的话我们的 uploads 文件 夹下应该就会多一个文件夹这个文件夹里面就是存储的所有文件的分片了。文件合并前端实现 核心切片合并 前端只需要向服务器发送一个合并的请求并且为了区分要合并的文件需要将文件的 hash 值给传过去/*** 发请求通知服务器合并切片*/ const mergeRequest () {// 发送合并请求fetch(http://127.0.0.1:3000/merge, {method: POST,headers: {Content-Type: application/json,},body: JSON.stringify({size: CHUNK_SIZE,fileHash: fileHash.value,fileName: fileName.value,}),}).then((response) response.json()).then(() {alert(上传成功)}) }文件合并后端实现 之前已经将所有的切片上传到服务器并存储到对应的目录里面去了 合并的时候需要从对应的文件夹中获取所有的切片然后利用文件的读写操作实现文件的合并了。 合并完成之后我们将生成的文件以 hash 值命名存放到对应的位置就可以了// 提取文件后缀名 const extractExt (filename) {return filename.slice(filename.lastIndexOf(.), filename.length) }/*** 读的内容写到writeStream中*/ const pipeStream (path, writeStream) {return new Promise((resolve, reject) {// 创建可读流const readStream fse.createReadStream(path)readStream.on(end, async () {fse.unlinkSync(path)resolve()})readStream.pipe(writeStream)}) }/*** 合并文件夹中的切片生成一个完整的文件*/ async function mergeFileChunk(filePath, fileHash, size) {const chunkDir path.resolve(UPLOAD_DIR, fileHash)const chunkPaths await fse.readdir(chunkDir)// 根据切片下标进行排序// 否则直接读取目录的获得的顺序可能会错乱chunkPaths.sort((a, b) {return a.split(-)[1] - b.split(-)[1]})const list chunkPaths.map((chunkPath, index) {return pipeStream(path.resolve(chunkDir, chunkPath),fse.createWriteStream(filePath, {start: index * size,end: (index 1) * size,}),)})await Promise.all(list)// 文件合并后删除保存切片的目录fse.rmdirSync(chunkDir) }// 合并文件 app.post(/merge, async (req, res) {const { fileHash, fileName, size } req.bodyconst filePath path.resolve(UPLOAD_DIR, ${fileHash}${extractExt(fileName)})// 如果大文件已经存在则直接返回if (fse.existsSync(filePath)) {res.status(200).json({ok: true,msg: 合并成功,})return}const chunkDir path.resolve(UPLOAD_DIR, fileHash)// 切片目录不存在则无法合并切片报异常if (!fse.existsSync(chunkDir)) {res.status(200).json({ok: false,msg: 合并失败请重新上传,})return}await mergeFileChunk(filePath, fileHash, size)res.status(200).json({ok: true,msg: 合并成功,}) })文件秒传断点续传 服务器上给上传的文件命名的时候就是用对应的 hash 值命名的 所以在上传之前判断有对应的这个文件就不用再重复上传了 直接告诉用户上传成功给用户的感觉就像是实现了秒传。文件秒传-前端 前端在上传之前需要将对应文件的 hash 值告诉服务器看看服务器上有没有对应的这个文件 如果有就直接返回不执行上传分片的操作了/*** 验证该文件是否需要上传文件通过hash生成唯一改名后也是不需要再上传的也就相当于秒传*/ const verifyUpload async () {return fetch(http://127.0.0.1:3000/verify, {method: POST,headers: {Content-Type: application/json,},body: JSON.stringify({fileName: fileName.value,fileHash: fileHash.value,}),}).then((response) response.json()).then((data) {return data // data中包含对应的表示服务器上有没有该文件的查询结果}) }// 点击上传事件 const handleUpload async (e: Event) {// ...// uploadedList已上传的切片的切片文件名称const res await verifyUpload()const { shouldUpload } res.dataif (!shouldUpload) {// 服务器上已经有该文件不需要上传alert(秒传上传成功)return}// 服务器上不存在该文件继续上传uploadChunks(fileChunks) }文件秒传-后端 // 根据文件hash验证文件有没有上传过 app.post(/verify, async (req, res) {const { fileHash, fileName } req.bodyconst filePath path.resolve(UPLOAD_DIR, ${fileHash}${extractExt(fileName)})if (fse.existsSync(filePath)) {// 文件存在服务器中不需要再上传了res.status(200).json({ok: true,data: {shouldUpload: false,},})} else {// 文件不在服务器中就需要上传res.status(200).json({ok: true,data: {shouldUpload: true,},})} })文件断点续传-前端 如果我们之前已经上传了一部分分片了我们只需要再上传之前拿到这部分分片 然后再过滤掉是不是就可以避免去重复上传这些分片了也就是只需要上传那些上传失败的分片 所以再上传之前还得加一个判断。 我们还是在那个 verify 的接口中去获取已经上传成功的分片然后在上传分片前进行一个过滤const uploadChunks async (fileChunks: Array{ file: Blob }, uploadedList: Arraystring) {const formDatas fileChunks.filter((chunk, index) {// 过滤服务器上已经有的切片return !uploadedList.includes(${fileHash.value}-${index})}).map(({ file }, index) {const formData new FormData()// 切片文件formData.append(file, file)// 切片文件hashformData.append(chunkHash, ${fileHash.value}-${index})// 大文件的文件名formData.append(fileName, fileName.value)// 大文件hashformData.append(fileHash, fileHash.value)return formData})// ... }文件断点续传-后端 只需在 /verify 这个接口中加上已经上传成功的所有切片的名称就可以 因为所有的切片都存放在以文件的 hash 值命名的那个文件夹 所以需要读取这个文件夹中所有的切片的名称就可以。/*** 返回已经上传切片名* param {*} fileHash* returns*/ const createUploadedList async (fileHash) {return fse.existsSync(path.resolve(UPLOAD_DIR, fileHash))? await fse.readdir(path.resolve(UPLOAD_DIR, fileHash)) // 读取该文件夹下所有的文件的名称: [] }// 根据文件hash验证文件有没有上传过 app.post(/verify, async (req, res) {const { fileHash, fileName } req.bodyconst filePath path.resolve(UPLOAD_DIR, ${fileHash}${extractExt(fileName)})if (fse.existsSync(filePath)) {// 文件存在服务器中不需要再上传了res.status(200).json({ok: true,data: {shouldUpload: false,},})} else {// 文件不在服务器中就需要上传并且返回服务器上已经存在的切片res.status(200).json({ok: true,data: {shouldUpload: true,uploadedList: await createUploadedList(fileHash),},})} })
http://www.hkea.cn/news/14365697/

相关文章:

  • 网站开发主流程序网站开发实习过程
  • 教你如何建网站视频百度云建站WordPress
  • 网站开发代做帮朋友做网站的坑
  • 网站建设的域名网络公司取名字参考大全
  • 获得网站后台地址需要优化的地方
  • 餐饮加盟什么网站建设如何开通微信小程序商城
  • 论坛网站制作模板奢侈品商城网站建设方案
  • 网站建设百度资源一站式快速网站排名多少钱
  • 重庆制作网站首页微站
  • 网站的二级网页关键词高端品牌手表
  • 门户网站建设目标网站虚拟机可以自己做吗
  • 网站的建设及维护报告青岛网站空间
  • 网页和网站的区别做一个购物网页
  • 遵义市和城乡建设局网站免费做金融网站有哪些
  • 淘宝店铺网站建设可行性报告网站上做网页怎么改图片
  • 网站建设公司好不好宁德seo培训
  • 网站建设主题的确定wordpress 短视频
  • 泉州网站开发国内软件外包公司
  • iis网站跳转专业做网站哪家正规
  • 金湖网站制作长春做网站 长春万网
  • 互联网站备案登记表湖州网站seo优化
  • 怎么做网站代销什么网站流量大
  • 公司网站建设全包wordpress清空文章备份并对齐id
  • 嘉兴网站制作计划建站要多少钱
  • 门户网站开发建设成本明细别人帮做的网站到期续费
  • 淘宝网站推广方案深圳市网站建设外包公司排名
  • 苏州网站制作 网站wordpress 显示当前时间
  • 没有公网ip做网站适合代码新手做的网站
  • 公司网站应该怎么做网站建设类型报价表
  • 网站内链是什么电脑上如何做网站