web前端

js大文件断点续传

2020-03-28  本文已影响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

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

上一篇 下一篇

猜你喜欢

热点阅读