美文网首页web前端
js大文件断点续传

js大文件断点续传

作者: 姜治宇 | 来源:发表于2020-03-28 16:52 被阅读0次

    以1G的电影为例,断点续传功能的思路是:
    1、前端将电影切成1024份小片,每份大小是1m
    2、前端将切片文件进行递归上传。
    3、后端接收文件,将字节流追加写入upload目录
    4、如果打断文件上传,先去后端查询已接收文件的大小,回传给前端
    5、slice的start位置更新为断点处,继续上传。
    6、后端接收完毕,返回success。
    7、前端显示进度100%完成。
    思路还是比较清晰的,我们先来做一下前端工作。浏览器提供的File对象有slice切片功能,我们可以根据文件大小切成小片。
    前端代码fileupload.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div class="con">
            <input type="file" id="upload" /><br>
    
    
            预览:<img id="showPic" src="" style="width:200px;height:200px;" /><br>
    
            <button onclick="upload()">点击上传</button>
    
            <progress id="prog" max="100" value="0"></progress>
        </div>
    
    </body>
    </html>
    <script>
        const chunkSize = 1024 * 1024 //每片大小为1MB
        var fileNode = document.getElementById('upload')//文件
        var progress = document.getElementById('prog')//进度条
        var fileObj;
        fileNode.onchange = function(e){
            fileObj = e.target.files[0]
            var fr = new FileReader()
            fr.readAsDataURL(fileObj)
            fr.onload = function(){
                document.getElementById('showPic').setAttribute('src',fr.result)
            }
    
        }
        function upload(){
    
            var fileSize = fileObj.size //文件大小
            var xhr = new XMLHttpRequest()
            //查询起始位置start
            xhr.open('post','/upload/filesize',true)
            xhr.onload = function(){
                if(this.readyState ===4 && this.status === 200) {
                    var start = parseInt(this.responseText)
    
                    progress.max = fileSize
                    progress.value = start
    
                    //开始上传
                    doUpload(start)
    
                }
            }
            var data = new FormData()
            data.append('filename',fileObj.name)
            xhr.send(data)
        }
        function doUpload(start){
            if(start >= fileObj.size){ //递归,设置退出条件
                return;
            }
            var end = (start + chunkSize > fileObj.size) ? fileObj.size : (start + chunkSize);
            var fd = new FormData()
            fd.append('filename',fileObj.name)
            fd.append('fileData',fileObj.slice(start,end))
    
            var xhr = new XMLHttpRequest()
            xhr.open('post','/upload/do',true)
            xhr.onload = function(){
                if(this.readyState ===4 && this.status ===200){
    
                    progress.value = end;
    
                    doUpload(end);
                }
            }
            xhr.send(fd)
    
        }
    </script>
    

    前端代码比较简单,主要是计算出切片start和end位置,然后递归调用自身即可。
    下面做一下后端,我们需要先搭建一个express服务器。

    1、下载安装node
    2、安装express
    npm install express -g
    
    3、安装express项目生成器
    npm install express-generator -g
    
    4、创建工程
    express --view=ejs myserver
    
    5、安装依赖包
    cd myserver
    npm install
    npm install multiparty -S
    
    6、运行服务
    npm start
    

    因为前端发送的是formdata格式,因此后端需要单独安装multiparty中间件来处理。
    当服务器跑起来之后:
    1、我们需要把前端代码fileupload.html迁移到public文件夹下面,通过网址http://localhost:3000/fileupload.html访问。
    2、在app.js新建一个路由upload。

    var createError = require('http-errors');
    var express = require('express');
    var path = require('path');
    var cookieParser = require('cookie-parser');
    var logger = require('morgan');
    
    var indexRouter = require('./routes/index');
    var usersRouter = require('./routes/users');
    var uploadRouter = require('./routes/upload');
    
    var app = express();
    
    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'ejs');
    
    app.use(logger('dev'));
    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, 'public')));
    
    app.use('/', indexRouter);
    app.use('/users', usersRouter);
    app.use('/upload', uploadRouter);
    
    // catch 404 and forward to error handler
    app.use(function(req, res, next) {
      next(createError(404));
    });
    
    // error handler
    app.use(function(err, req, res, next) {
      // set locals, only providing error in development
      res.locals.message = err.message;
      res.locals.error = req.app.get('env') === 'development' ? err : {};
    
      // render the error page
      res.status(err.status || 500);
      res.render('error');
    });
    
    module.exports = app;
    

    3、在routes下面新增文件夹upload,用来放上传的文件。
    4、routes/upload.js:

    var express = require('express');
    var router = express.Router();
    var path = require('path');
    var fs = require("fs")
    var multiparty = require('multiparty');
    
    var uploadPath = path.join(__dirname, 'upload','/');
    
    router.post('/filesize', function(req, res, next) {
        var form = new multiparty.Form();
        form.parse(req, function(err, fields, files) {
    
            if(!err){
                var filePath = path.join(uploadPath, fields.filename[0]);
                fs.stat(filePath,function(error,stats){
                    console.log(stats)
                    if(error){
                        res.send('0')
                    }else{
                        res.send(''+stats.size);
                    }
                })
            }
    
        })
    
    });
    router.post('/do', function(req, res, next) {
        var form = new multiparty.Form();
        form.parse(req, function(err, fields, files) {
            if(!err){
                console.log(fields)//除文件之外的字段信息
                console.log(files)//文件相关的信息
    
                var fileData = files.fileData[0]
                var filePath = path.join(uploadPath, fields.filename[0]);
    
                fs.appendFileSync(filePath, fs.readFileSync(fileData.path));//同步读写数据
                res.send('success')//返回成功状态
            }
        })
    
    });
    
    module.exports = router;
    
    经过测试发现,文件确实是分片上传了: upload.jpg

    你可以在上传中途刷新一下页面,然后重新上传该文件,发现进度条仍是从断点处开始上传的。

    相关文章

      网友评论

        本文标题:js大文件断点续传

        本文链接:https://www.haomeiwen.com/subject/mstluhtx.html