node实现文件分片上传

前端在做文件上传时,考虑到网速的快慢,如果文件过大的话可能会导致上传时间过长而请求超时,文件上传失败。因此文件过大需要对文件进行分片上传。

那文件分片上传的具体过程是怎样的呢?

进行了许多搜索查找之后,参照众多资源进行修改,得到了自己的简易实现流程。

首先列出来node需要用到的模块:

const express = require('express');
var multer = require('multer');
var fs=require('fs');
var path = require('path');
var app = express();
var fse = require('fs-extra');
{"name": "fileloader","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","start": "nodemon index.js"},"keywords": [],"author": "","license": "ISC","dependencies": {"art-template": "^4.13.2","express": "^4.17.1","express-art-template": "^1.0.1","fs-extra": "^9.1.0","jquery": "^3.6.0","multer": "^1.4.2"}
}

服务器实例采用express框架快速搭建。配合art-template 和 express-art-template进行页面处理

multer 模块用于处理文件上传

fs-extra模块倒不是必须的,只是参照其他的文章,采用这个模块来删除文件夹较为方便。

multer配置介绍

multer用于作为处理文件上传的中间件,可以通过 var upload = multer({dest: '路径'})来实例化multer,然后在路由中配置upload.single('file') 作为中间件。如官方的文档介绍所示:

var express = require('express')
var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })var app = express()app.post('/profile', upload.single('avatar'), function (req, res, next) {})app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {})var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])app.post('/cool-profile', cpUpload, function (req, res, next) {})

官方的示例中,upload实例有三种主要的使用形式,即upload.single()、upload.array()、upload.fileds(),这三者简单来说应该是single用于处理单文件,array和fieds都能处理多文件,由于项目仅演示单文件处理过程,所以这两个不过多赘述,想要了解更多请阅读multer中文翻译文档

.single(filename) --> 接受一个以 fieldname 命名的文件。这个文件的信息保存在 req.file

另外,multer也会向req中添加body字段,如果想要将multer作为body-parser的替代,需要配置类似如下的路由

app.post('/merge',upload.none(),function(req,res){})

此外,如果前端使用jquery的ajax进行上传,需要配置一些特殊的选项,传递的数据也需要是一个 FormData()对象,我不知道这是不是必须,但是我用这种方法暂时未出现问题,这也是我用multer替代body-parser 出现的问题。

当然,这只是简单地配置,如果需要对文件存储路径和文件名进行特殊的设置,需要配置 storage 参数,这是磁盘存储引擎,可以控制文件的存储。

它有两个选项可用,destination 和 filename。他们都是用来确定文件存储位置的函数。

destination 是用来确定上传的文件应该存储在哪个文件夹中。也可以提供一个 string (例如 '/tmp/uploads')。如果没有设置 destination,则使用操作系统默认的临时文件夹。

注意: 如果你提供的 destination 是一个函数,你需要负责创建文件夹。当提供一个字符串,multer 将确保这个文件夹是你创建的。

filename 用于确定文件夹中的文件名的确定。 如果没有设置 filename,每个文件将设置为一个随机文件名,并且是没有扩展名的。

代码示例

前端代码示例:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>document</title><style>body,div,p,h1,h2,h3,h4,h5,h6,ul,li{margin:0;padding:0;list-style:none;}ul,ol{list-style:none;}input{outline-style: none;padding: 0;}.wrap{width: 600px;height: 400px;background-color: lightblue;border-radius: 20px;margin: 50px auto;}label[for="file"]{display: inline-block;width: 100px;height: 100px;background-color: lightcoral;text-align: center;/* vertical-align: middle; */line-height: 100px;}#file{opacity: 0;}input[type="button"]{width: 80px;height: 32px;margin: 10px 10px;background-color: rgba(0, 0, blue, 0.5);outline-style: none;}</style>
</head>
<body><div class="wrap"><div class="form"><label for="file">选择文件</label><input type="file" id="file" name="file" accept="*" ><br><input type="button" value="上传" onclick="upload(0)"></div></div><script src="/node_modules/jquery/dist/jquery.js"></script><script>var chunkSize = 1024*1024;var fileInput = document.querySelector("#file");function upload(index){let files = fileInput.files;if(!files[0]){alert("请选择文件")return }let file = files[0];// file对象//获取文件名和扩展名let [fname,fext] = file.name.split('.')// 分片let start = index * chunkSizeif(start > file.size){merge(file.name);return;}let blob = file.slice(start,start + chunkSize)let blobName = `${fname}.${index}.${fext}`;let blobFile = new File([blob],blobName);// 上传文件let formData = new FormData()formData.append('file',blobFile);$.ajax({type: 'post',url: '/upload',data: formData,contentType: false,processData: false,success: function(res){console.log(res);upload(++index);//递归上传文件分片}})}// 合并文件请求function merge(filename){var formData = new FormData()formData.append('name',filename)$.ajax({type: 'post',url:"/merge",data: formData,contentType: false,processData: false,dataType: 'json',success: function(res){console.log(res);}})}</script>
</body>
<html>

通过input选择框,用户选择文件后该元素返回一个File对象,存在input.files中。

File对象继承自Blob对象,拥有slice方法,返回一个blob子对象,是file的一个切片。

得到文件分片后,设置其分片的顺序,并将其添加到一个FormData对象中。

用jquery的ajax进行上传,需要配置两个参数:

contentType: false,
                processData: false,

不配置可能会报错。

所有分片上传之后,需要发送请求进行文件合并,函数merge即为请求合并文件的函数。

后台代码:

const express = require('express');
var multer = require('multer');
var fs=require('fs');
var path = require('path');
var app = express();
var fse = require('fs-extra');// 用于检测是否存在用于存放文件的路径,不存在则创建路径
const createFolder = function(folder){try{fs.accessSync(folder);}catch(e){fs.mkdirSync(folder);}
};
// 文件上传的路径
var uploadFolder = './uploads/';
createFolder(uploadFolder);// 用于传给multer进行复杂的文件上传配置
var storage = multer.diskStorage({destination: function(req,file,cb){// 用于进行复杂的路径配置,此处考虑分片上传,先将分片文件保存在临时目录中let [fname,index,fext] = file.originalname.split(".");let chunkDir = `${uploadFolder}/${fname}`;if(!fse.existsSync(chunkDir)){fse.mkdirsSync(chunkDir);}cb(null,chunkDir); //内部提供的回调函数 },filename: function(req,file,cb){// 根据上传的文件名,按分片顺序用分片索引命名,// 由于是分片文件,请不要加扩展名,在最后文件合并的时候再添加扩展名let fname = file.originalname;cb(null,fname.split('.')[1]);}
})var upload = multer({storage: storage});// multer实例// 配置模板引擎
app.engine('html',require('express-art-template'))
app.set('views',__dirname+'/views')// 静态资源路由
app.use('/node_modules',express.static('node_modules'))
app.use('/upload',express.static('uploads'))// 主页路由
app.get('/',function(req,res){res.render('index.html')
})// 上传路由
app.post('/upload',upload.single('file'),function(req,res,next){res.end("ok");
})// 文件合并路由
app.post('/merge',upload.none(),function(req,res){let name = req.body.name;let fname = name.split('.')[0];let chunkDir = path.join(uploadFolder,fname);let chunks = fs.readdirSync(chunkDir); // 同步读取以防文件合并顺序混乱chunks.sort((a,b)=>a-b).map(chunkPath=>{fs.appendFileSync(path.join(uploadFolder,name),fs.readFileSync(`${chunkDir}/${chunkPath}`))})fse.removeSync(chunkDir);res.send({msg:'合并成功',url:`http://localhost:8080/upload/${name}`});
})app.listen(8080,()=>{console.log("success.....localhost:8080")
})

配置 multer的磁盘存储引擎时, 在destination中设置了保存分片文件的临时文件夹,在filename 中设置了按照分片索引来存储文件,不添加扩展名。

在 merge 路由中进行文件合并,同步读取文件临时目录,用 fs 模块对文件进行排序之后进行文件合并。

项目地址:我的gitee仓库

node实现文件分片上传之multer篇相关推荐

  1. 大文件分片上传前端框架_基于Node.js的大文件分片上传

    基于Node.js的大文件分片上传 我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况.所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作.同时如果文件过大,在网络不佳 ...

  2. 大文件分片上传,断点续传,秒传 实现

    前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...

  3. jquery 分片上传php,php 大文件分片上传

    前端部分 上传 //上传控件 uploadBig('upload','zip,rar,7z,tar',{ id: '', type: 'upload_file', } ,(res)=>{ //t ...

  4. Vue项目中遇到了大文件分片上传的问题

    Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽. 上传就上传吧,为什么 ...

  5. 大文件分片上传前端框架_无插件实现大文件分片上传,断点续传

    文件上传.gif 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试上传等需求的使用场景及实现: 2. 项目需求 在一个音视频的添加中,既要有音视频 ...

  6. 无插件实现大文件分片上传,断点续传

    代码地址如下: http://www.demodashi.com/demo/11888.html 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试 ...

  7. java加vue实例_Vue.Js及Java实现文件分片上传代码实例

    upload(file) { //从后台获取已经上传的文件分片数 getIdx(md5) .then(function(res) { let retry = 3; uploadPart(retry, ...

  8. 使用webuploader组件实现大文件分片上传,断点续传

    无组件断点续传.gif 1. 组件简介 webuploader:是一个以HTML5为主, Flash为辅的文件上传组件,采用大文件分片/并发上传的方式,极大地提高了文件上传的效率,同时兼容多种浏览器版 ...

  9. html上传视频文件前端显示,文件分片上传之前端文件分片

    分片上传的原理就是在前端将文件分片,然后一片一片的传给服务端,由服务端对分片的文件进行合并,从而完成大文件的上传.分片上传可以解决文件上传过程中超时.传输中断造成的上传失败,而且一旦上传失败后,已经上 ...

最新文章

  1. R 数据可视化 : 热图
  2. dram和nand哪个难生产_DRAM与NAND差别这么大,存储之争都争啥?-嵌入式系统-与非网...
  3. linux7.4 配置yum,Centos7.4重装yum
  4. C#+WebSocket+WebRTC多人语音视频系统
  5. druid mysql配置详解_druid配置详解
  6. 元宇宙iwemeta: 苹果Apple推动元宇宙产业的生态体系
  7. Web前端之登录表单
  8. 汇编语言程序如何转化成c语言,如何把汇编语言转换成C语言
  9. 【收藏】Vue+elementUI的this.$refs.对象名.方法名的理解
  10. boost::graph::distributed::mpi_process_groupboost::graph::用法的测试程序
  11. 转:Ubuntu下ibus-sunpinyin的安装及翻页快捷键设置!
  12. Erlang 位串和二进制数据
  13. 生物大数据时代,如何做好数据管理和再利用,发IF10+的数据库文章?
  14. mysql 免安装重装_mysql 重装问题
  15. 针对自动化测试的23种Node.js优秀实践
  16. 矩阵微分(matrix derivatives)
  17. java 综合练习_Java 之综合练习
  18. python 坦克大战游戏
  19. 视频教程-初级学习ArcGIS Engine视频课程-C#
  20. 常见的路由器(刷openwrt的同学看下!)

热门文章

  1. 【微服务】复杂系统:微服务与人类
  2. python学习指南—Python 进阶(Python Cookbook)
  3. 继BAT之后 第四大巨头是谁:京东、360还是小米?
  4. 我的2010工作与爱情
  5. 南京大学_计算机系_夏令营_机试
  6. 蓝桥杯——我该如何枚举
  7. 华为mate手机从解锁到root成功全步骤
  8. 实现科汛CMS在标签中调用作者【已解决】
  9. 蓝桥杯试题:核桃的数量(C/C++)
  10. 当CPU过热时让你的风扇不再狂转(Rootkit之最后)