资讯专栏INFORMATION COLUMN

分片上传与断点续传解决方案

includecmath / 1215人阅读

摘要:所以,该文适合于全栈工程师,至少是想成为还有,为什么需要分片,不分片能实现断点续传吗分片是为了充分利用网络带宽,加快上传速度不分片也是能够实现断点续传的。详细参考文件上传组件深度剖析分片上传与断点续传之间没有很直接的关系好了,进入正题。

上传文件,基本上是每一个网站应用都会具备的一个功能。对于一个网络存储应用,对于上传功能要求更是迫切。

如今市面上成熟上传插件,如WebUploader,"体积太大",不适合于移动端上传;再加上作为一位程序员的"操守",当然还是更喜欢自己造轮子。

于是花了一天半时间,MoUploader应运而生。为什么叫MoUploader呢?Mo表示Mobile(其实更是因为我的绰号moyu)

关于实现原理

首先需要明确,上传这东西不仅仅是只需要前端就能完成的很好的,需要前端后端统一数据格式,从而实现断点续传。(所以,该文适合于全栈工程师,至少是想成为)

还有,为什么需要分片,不分片能实现断点续传吗?分片是为了充分利用网络带宽,加快上传速度;不分片也是能够实现断点续传的。详细参考 HTML5文件上传组件深度剖析.
分片上传与断点续传之间没有很直接的关系.

好了,进入正题。

实现断点续传的前提是需要服务器记录某文件的上传进度,那么根据什么判断是不是同一个文件呢?可以利用文件内容求md5码,如果文件过大,求取md5码也是一个很长的过程,所以对于大文件,只能针对某一段数据进行计算,加上服务器对cookie用户信息的判断,得到相对唯一的key。

在前端页面,需要将文件按照一定大小进行分片,一次请求只发送这一小片数据,所以我们可以同时发起多个请求。但一次同时请求的连接数不宜过多,服务器负载过重。对于文件分片操作,H5具有十分强大的File API,直接利用File对象的slice方法即可得到Blob对象。

至于同时传输数据的连接数控制逻辑,就需要花点脑子思考了。前端把数据顺利得传给服务器了,服务器只需要按照数据中给的开始字节位置,与读取到的文件片段数据,写入文件即可

更多信息就看源码吧!MoUploader

功能实现

文件结构

file-upload/
├── bower_components/ # bower包
├── db.js   # 数据操作接口
├── demo.html
├── md5.json # 数据
├── mouploader.js # 源码
├── README.md 
└── server.js # demo.html服务, 建立在3000端口

1 directories, 8 files.

(打印文件目录树使用的是自己写的print-dir)

怎么使用

引入script,amd/cmd/...,

使用MoUploader

input.onchange = function (e) {
    var self = this;
    var moUploader = MoUploader({ 
        files: this.files,
        uploadUrl: "/upload",
        request: false,
        onBeforeUpload: function (index) {
            if(index>=0) {
                self.files[index].progress = appendUploading(self.files[index], index)
            }
        },
        onOverAllProgress: function (index, loaded, total) {
            console.log(loaded / total)
            //setProgress(loaded / total, self.files[index].progress)
        },
        onLoad: function (index, chunkIndex, chunksNum) {
            console.log("onLoad", this, arguments)
        },
        onAbort: function (index, chunkIndex, chunksNum) {
            console.log("onAbort", this, arguments)
        },
        onError: function (index, chunkIndex, chunksNum) {
            console.log("onError", this, arguments)
        },
        onContinue: function (file, md5, index) {
            return new Promise(function(reslove, reject) {
                var xhr = new XMLHttpRequest()
                xhr.open("GET", "/getFile?md5="+md5, true);
                xhr.send(null);
                xhr.addEventListener("readystatechange", function () {
                    if(xhr.readyState === 4 && xhr.status === 200) {
                        var json = JSON.parse(xhr.responseText);
                        log(json)
                        reslove(json.pos)
                    }
                })
            })
        }
    })
    
    // pause or continue upload
    // if index < 0, will run for all files
    // moUploader.pause(index);
    // moUploader.continue(index);    
}

配置选项

var default_ops = {
    // chunk Size: byte
    chunkSize: (1<<20) * 5,
    // Number: request Number.
    // Array: files requests.
    // Boolean: open or close Slice, if false, chunkSize don"t work.
    request: 3,
    files: [],
    uploadUrl: "/",
    // function: get uploaded pos.
    // arguments: file, md5, index.
    // need return a promise object which will return uploaded pos.
    onContinue: null,
    // if false, md5 will be setted by filename.
    md5: true,
    // md5Size: slice file 0 - md5Size for calculate md5
    md5Size: (1<<20) * 50,
    // called when before upload.
    // arguments: file index or -1 (will begin upload)
    onBeforeUpload: null,
    // function: uploading progress listener.
    // *only listen one request.*
    // arguments: index, chunkIndex, chunksNum, loaded, total.
    onProgress: null,
    // function: overall uploading progress listener.
    // arguments: index, loaded, total
    onOverAllProgress: null,
    // function: called when one request is ended.
    // arguments: index, chunkIndex, chunksNum
    onLoad: null,
    // function: called when one request is aborted.
    // arguments: index, chunkIndex, chunksNum
    onAbort: null,
    // function: called when one request happens error.
    // arguments: index, chunkIndex, chunksNum
    onError: null
}

服务器数据处理 (Node.js)
数据分段写入文件

function writeBuffer(bf, path, pos) {
    var fd = fs.openSync(path, "a+");
    fs.writeSync(fd, bf, 0, bf.length, Number(pos) || 0)
    console.log(`write buffer, pos: ${pos}, path: ${path}, length: ${bf.length}`)
}

function store(param, chunks) {
    param.chunks = param.chunks || 1
    param.chunk = param.chunk || 0
    var p = path.join("./upload", param.name)
    var bf = Buffer.concat(chunks);

    var json = db.get(param.md5);
    if(json) {
        json.pos = parseInt(json.pos!=null?json.pos : 0)
        json.size = parseInt(json.size!=null?json.size : 0)
    }
    if(!json || (json.pos+json.size) <= param.pos) {
        // 新的数据pos比数据库中大,更新数据
        param.size = bf.length
        db.set(param.md5, param)
        db.save();
        writeBuffer(bf, p, param.pos || 0)
    }
}

var multiparty = require("multiparty")
var form = new multiparty.Form({
    autoFields: true,
    autoFiles: false,
});

form.on("part", (part) => {
    form.on("aborted", () => {
        //意外退出或者暂停都会保存数据
        console.log("aborted");
        store(param, chunks)
    })

    var chunks = []
    part.on("data", (data) => {
        if(part.filename) {
            chunks.push(data)
        }
    }).on("end", () => {
        console.log("end")
        store(param, chunks)
    })

});
form.on("field", (name, value) => {
    param[name] = value;
});

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87909.html

相关文章

  • 基于Node.js的大文件分片上传

    摘要:基于的大文件分片上传我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况。所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作。所以把文件名称加上。后续延伸断点续传多文件多批次上传 基于Node.js的大文件分片上传 我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况。所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作。同时如...

    Genng 评论0 收藏0
  • 基于Node.js的大文件分片上传

    摘要:基于的大文件分片上传我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况。所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作。所以把文件名称加上。后续延伸断点续传多文件多批次上传 基于Node.js的大文件分片上传 我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况。所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作。同时如...

    lcodecorex 评论0 收藏0

发表评论

0条评论

includecmath

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<