用Electron开发企业网盘(二)--分片下载
书接上文,背景见:https://www.cnblogs.com/shawnyung/p/10060119.html
HTTP请求头 Range
请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始。
如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明.
如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK 。
Range请求头格式
Range: bytes=start-end
响应头
Conent-Length
表示这次服务器响应数据的字节数
一、思路整理
用过迅雷等下载工具会发现:文件在下载过程中,会生成.downloading后缀和.downloading.cfg后缀的两个文件。.downloading后缀的文件跟文件已下载的大小是一致的,而.downloading.cfg后缀的文件特别小。当文件下载完成后,.downloading后缀及.downloading.cfg文件均不存在,只保留下载完成的文件。
通过网上了解知道,cfg文件大多是配置文件。那么可以 推测出:.downloading文件是下载的临时文件,接收下载文件流。而.downloading.cfg是下载的配置文件,保存文件下载的相关信息。
配合断点续传的需求,梳理出分片下载的方案:文件下载,首先判断当前目录有没有已下载的断点文件。若有,则创建一个'append'的文件流,通过.downloading.cfg文件读取已下载分片的相关信息,续传下载;若无,则创建一个新文件流,指定请求文件的部分内容(分片)。传输过程中,将文件流写入.downloading文件,并同步更新.downloading.cfg文件,记录下载文件的相关信息及分片信息。每一片传输完成,判断服务器相应数据的字节是否小于分片字节数。若是,表示为最后一个分片,文件已下载完成,将.downloading文件重命名为原文件名并删除.downloading.cfg文件。
二、分解任务
将任务分解成几个子任务:
1、递归创建文件夹。
2、判断当前目录有没有已下载的断点文件,创建文件流。
3、设定HTTP请求头Range,分片请求文件url。
4、更新.downloading.cfg文件。
5、文件下载完成,重命名.downloading文件并删除.downloading.cfg文件。
1、递归创建文件夹
完整路径为“D:/tmp/新建文件夹/002.docx”之类的文件在下载时需要先一级一级创建文件夹。借助Node的fs及path模块,完成递归创建文件夹任务。
const fs = require("fs")
const path = require("path")
const mkdirs = (dirname, callback, errback) => {
fs.stat(dirname, (err, stats) => {
if (err) {
mkdirs(path.dirname(dirname), () => {
fs.mkdir(dirname, callback)
}, errback)
} else {
if (stats.isDirectory()) {
callback()
} else {
errback()
}
}
})
}
2、父级文件夹创建好后,判断当前目录有没有已下载的断点文件,创建文件流。
fs.createWriteStream返回WriteSteam对象,用于创建文件写入流。
fs.createWriteStream(path[, options])
path<string> | <Buffer> | <URL>- Returns: <fs.WriteStream> See Writable Stream.
还是借助Node的fs模块的stat方法,检测当前目录有没有.downloading文件。若有,则创建一个flags为'a'的文件流;若无,则创建一个默认的文件流。
let statDir = function (flag) {
fs.stat(file.path + '.downloading', function (err, stats) {
if (flag) {
if (err) {
contents.send('download-error', file.path)
stream.end()
return
}
} else {
stream = !err ? fs.createWriteStream(file.path + '.downloading', {flags: 'a'}) :
fs.createWriteStream(file.path + '.downloading')
streams.push(stream)
if (!err) {
receivedBytes += stats.size
}
}
func()
})
}
3、设定HTTP请求头Range,分片请求文件url。
net
使用Chromium的原生网络库发出HTTP / HTTPS请求
net 模块是一个发送 HTTP(S) 请求的客户端API。 它类似于Node.js的HTTP 和 HTTPS 模块 ,但它使用的是Chromium原生网络库来替代Node.js的实现,提供更好的网络代理支持。
receivedBytes为.downloading临时文件已下载的文件流大小,chunkSize为分片大小。所以每个分片的请求内容为receivedBytes至receivedBytes + chunkSize - 1。每个分片下载完成后,更新receivedBytes大小。
const request = net.request(url)
let start = receivedBytes
let end = receivedBytes + chunkSize - 1
request.setHeader('Range', 'bytes=' + start + '-' + end)
request.on('response', (response) => {
response.on('data', chunk => {
if (response.statusCode == 206) {
try {
stream.write(chunk)
} catch(e) {}
}
}) let contentLength = response.headers['content-length'][0]
response.on('end', () => {
receivedBytes += parseInt(contentLength)
}
4、更新.downloading.cfg文件,记录下载文件的相关信息及分片信息。
.downloading文件保存文件的进度,大小,路径等信息。用于启动应用时,读取并渲染续传列表,显示文件名,文件大小,进度条等信息。
let json = {
percent: percent,
filesize: file.filesize,
md5: file.md5,
uid: file.uid ,
bucketName: file.bucketName,
path: file.path,
}
try {
!stream.closed && fs.writeFileSync(file.path + '.downloading.cfg', JSON.stringify(json))
} catch(e) {}
5、最后一个分片下载完成 ,将.downloading文件重命名为原文件名并删除.downloading.cfg文件。
getList
获取当前目录下的文件列表。
获取文件列表后,算出重命名后的文件名(如果当前目录有重名文件,则需要将文件重命名。重命名算法见系列文章(一))。将.downloading文件重命名为算出的文件名并删除.downloading.cfg文件。
if (contentLength < chunkSize) {
stream.end()
endStream(file.path)
try {
getList(dirname).then(fileList => {
let newName = fileRename(fileList, filename, 'filename')
setTimeout(() => {
fs.rename(file.path + '.downloading', path.join(dirname, newName), (err) => {
if (err) {
return console.error(err)
}
})
}, 500)
})
fs.unlink(file.path + '.downloading.cfg', function (er) {
if (er) {
return console.error(er);
}
})
} catch (e) { console.log(e) }
} else {
!stream.closed && statDir(true)
}
至此,文件分片下载完成。
用Electron开发企业网盘(二)--分片下载的更多相关文章
- 用Electron开发企业网盘(一)--通信
效果展示 项目背景: 由于浏览器的限制,web批量下载体验不好以及无法下载文件夹.采用Electron技术,通过js开发PC应用程序,着力解决批量下载.断点续传.文件夹下载等问题.配合网页版网盘使用, ...
- 【Electron】Electron开发入门(二):创建项目Hello Word
创建简单的Electron程序 1.首先,切换到你的项目空间,我的在 D:\ProjectsSpace\ElectronProjects\ElectronTest,ElectronTest是案例项目文 ...
- 【Electron】Electron开发入门
Electron简介: Electron提供了丰富的本地(操作系统)的API,使你能够使用纯JavaScript来创建桌面应用程序,并且跨平台(win,mac,linux等各种PC端平台).与其它各种 ...
- Android开发——通过扫描二维码,打开或者下载Android应用
Android开发——通过扫描二维码,打开或者下载Android应用 在实现这个功能的时候,被不同的浏览器折磨的胃疼,最后实现了勉强能用,也查考了一下其他人的博客 android实现通过浏览器点击 ...
- [实战]MVC5+EF6+MySql企业网盘实战(18)——文件上传,下载,修改
写在前面 经过一段时间的秀秀改改,终于把文件上传下载,修改文件夹文件名称的功能实现了. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySql企 ...
- 桌面应用之electron开发与转换
桌面应用之electron开发与转换 一,介绍与需求 1.1,介绍 1. Electron简介 Electron是用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Ele ...
- Electron入门笔记(二)-快速建立hello world
官方的文档我没有看懂,看了不少别人的博客和文章,终于慢慢看懂了如何快速的建立一个Electron app demo,前一篇文章不是使用官方快速搭建的,而且还出了小问题,所以去撸了一遍quick-sta ...
- [实战]MVC5+EF6+MySql企业网盘实战(8)——文件下载、删除
写在前面 上篇文章通过iframe实现了文件的无刷新上传.这篇我们将实现文件的下载与删除. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySq ...
- Electron开发跨平台桌面程序入门教程
最近一直在学习 Electron 开发桌面应用程序,在尝试了 java swing 和 FXjava 后,感叹还是 Electron 开发桌面应用上手最快.我会在这一篇文章中实现一个HelloWord ...
随机推荐
- 用pigz来加速解压tar.gz
兼容tar.gz 多线程的解压工具, 用于解压大文件时使用. https://zlib.net/pigz/ 方法: 1. 安装pigz 2. 使用tar时,选择pigz tar --use-compr ...
- MFC补码原码反码转换工具
/*_TCHAR str[100] = { 0 }; wsprintf(str, _T("%d"),num);*/ ; CString str; m_edit1.GetWindow ...
- PowerDesigner 16.5 安装及破解步骤
安装: 1.双击运行PowerDesigner16.5_Evaluation.exe,进入安装界面,点击(Next)下一步按钮: 2.下拉菜单选择HongKong,选中 I agree to the ...
- Android ViewPager系列之ViewPager一屏显示多个子页面
ViewPager一屏显示多个子页面,常见的有两种形式: 1.当前展示的页面右侧显示一部分下个页面的内容 2.当前页面居中,左右两边分别显示上一个页面.下一个页面 第 1 种表现形式的实现代码 其实这 ...
- C++ STL next_permutation() prev_permutation(a,a+n)用法。
int a[3] = {1,2,3}; a可能形成的集合为{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,1,2},{3,2,1}. {2,1,3}的prev是{1,3,2}, ...
- JavaScript学习——BOM对象
1.BOM 对象:浏览器对象模型(操作与浏览器相关的内容) 2.Window 对象 Window 对象表示浏览器中打开的窗口 setInterval():它有一个返回值,主要是提供给 clearInt ...
- hdu 1257/1800 - 贪心,dp
1257题目链接 一个序列划分子序列,每个子序列都是非增序列,问最少分成几个子序列 1800题目链接 一堆数分组,每组内数据严格递减,问最少分几组 -------------------------- ...
- Visual Studio中C++工程的环境配置方法
在Visual Studio的C++工程设置 1.添加工程的头文件目录:工程---属性---配置属性---c/c++---常规---附加包含目录. 2.添加文件引用的lib静态库路径:工程---属性- ...
- BZOJ 4870: [Shoi2017]组合数问题 矩阵乘法_递推
Code: #include <cstdio> #include <cstring> #include <algorithm> #define setIO(s) f ...
- 洛谷 P2633 Count on a tree 主席树
在一棵树上,我们要求点 $(u,v)$ 之间路径的第$k$大数. 对于点 $i$ ,建立 $i$ 到根节点的一棵前缀主席树. 简单容斥后不难得出结果为$sumv[u]+sumv[v]−sumv[l ...