书接上文,背景见:https://www.cnblogs.com/shawnyung/p/10060119.html

HTTP请求头  Range

  请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始。

  如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明.

  如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK 。

Range请求头格式

  1. 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模块,完成递归创建文件夹任务。

  1. const fs = require("fs")
  2. const path = require("path")
  3.  
  4. const mkdirs = (dirname, callback, errback) => {
  5. fs.stat(dirname, (err, stats) => {
  6. if (err) {
  7. mkdirs(path.dirname(dirname), () => {
  8. fs.mkdir(dirname, callback)
  9. }, errback)
  10. } else {
  11. if (stats.isDirectory()) {
  12. callback()
  13. } else {
  14. errback()
  15. }
  16. }
  17. })
  18. }

2、父级文件夹创建好后,判断当前目录有没有已下载的断点文件,创建文件流。

  fs.createWriteStream返回WriteSteam对象,用于创建文件写入流。

fs.createWriteStream(path[, options])

还是借助Node的fs模块的stat方法,检测当前目录有没有.downloading文件。若有,则创建一个flags为'a'的文件流;若无,则创建一个默认的文件流。

  1. let statDir = function (flag) {
  2. fs.stat(file.path + '.downloading', function (err, stats) {
  3. if (flag) {
  4. if (err) {
  5. contents.send('download-error', file.path)
  6. stream.end()
  7. return
  8. }
  9. } else {
  10. stream = !err ? fs.createWriteStream(file.path + '.downloading', {flags: 'a'}) :
  11. fs.createWriteStream(file.path + '.downloading')
  12. streams.push(stream)
  13. if (!err) {
  14. receivedBytes += stats.size
  15. }
  16. }
  17. func()
  18. })
  19. }

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大小。

  1. const request = net.request(url)
  2. let start = receivedBytes
  3. let end = receivedBytes + chunkSize - 1
  4. request.setHeader('Range', 'bytes=' + start + '-' + end)
  5. request.on('response', (response) => {
  6. response.on('data', chunk => {
  7. if (response.statusCode == 206) {
  8. try {
  9. stream.write(chunk)
  10. } catch(e) {}
  11. }
  12. })
  13.  
  14. let contentLength = response.headers['content-length'][0]
  15. response.on('end', () => {
  16. receivedBytes += parseInt(contentLength)
        }

4、更新.downloading.cfg文件,记录下载文件的相关信息及分片信息。

  .downloading文件保存文件的进度,大小,路径等信息。用于启动应用时,读取并渲染续传列表,显示文件名,文件大小,进度条等信息。

  1. let json = {
  2. percent: percent,
  3. filesize: file.filesize,
  4. md5: file.md5,
  5. uid: file.uid ,
  6. bucketName: file.bucketName,
  7. path: file.path,
  8. }
  9. try {
  10. !stream.closed && fs.writeFileSync(file.path + '.downloading.cfg', JSON.stringify(json))
  11. } catch(e) {}

5、最后一个分片下载完成 ,将.downloading文件重命名为原文件名并删除.downloading.cfg文件。

getList

获取当前目录下的文件列表。

  获取文件列表后,算出重命名后的文件名(如果当前目录有重名文件,则需要将文件重命名。重命名算法见系列文章(一))。将.downloading文件重命名为算出的文件名并删除.downloading.cfg文件。

  1. if (contentLength < chunkSize) {
  2. stream.end()
  3. endStream(file.path)
  4. try {
  5. getList(dirname).then(fileList => {
  6. let newName = fileRename(fileList, filename, 'filename')
  7. setTimeout(() => {
  8. fs.rename(file.path + '.downloading', path.join(dirname, newName), (err) => {
  9. if (err) {
  10. return console.error(err)
  11. }
  12. })
  13. }, 500)
  14. })
  15.  
  16. fs.unlink(file.path + '.downloading.cfg', function (er) {
  17. if (er) {
  18. return console.error(er);
  19. }
  20. })
  21. } catch (e) { console.log(e) }
  22. } else {
  23. !stream.closed && statDir(true)
  24. }

  至此,文件分片下载完成。

用Electron开发企业网盘(二)--分片下载的更多相关文章

  1. 用Electron开发企业网盘(一)--通信

    效果展示 项目背景: 由于浏览器的限制,web批量下载体验不好以及无法下载文件夹.采用Electron技术,通过js开发PC应用程序,着力解决批量下载.断点续传.文件夹下载等问题.配合网页版网盘使用, ...

  2. 【Electron】Electron开发入门(二):创建项目Hello Word

    创建简单的Electron程序 1.首先,切换到你的项目空间,我的在 D:\ProjectsSpace\ElectronProjects\ElectronTest,ElectronTest是案例项目文 ...

  3. 【Electron】Electron开发入门

    Electron简介: Electron提供了丰富的本地(操作系统)的API,使你能够使用纯JavaScript来创建桌面应用程序,并且跨平台(win,mac,linux等各种PC端平台).与其它各种 ...

  4. Android开发——通过扫描二维码,打开或者下载Android应用

    Android开发——通过扫描二维码,打开或者下载Android应用   在实现这个功能的时候,被不同的浏览器折磨的胃疼,最后实现了勉强能用,也查考了一下其他人的博客 android实现通过浏览器点击 ...

  5. [实战]MVC5+EF6+MySql企业网盘实战(18)——文件上传,下载,修改

    写在前面 经过一段时间的秀秀改改,终于把文件上传下载,修改文件夹文件名称的功能实现了. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySql企 ...

  6. 桌面应用之electron开发与转换

    桌面应用之electron开发与转换 一,介绍与需求 1.1,介绍 1. Electron简介 Electron是用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Ele ...

  7. Electron入门笔记(二)-快速建立hello world

    官方的文档我没有看懂,看了不少别人的博客和文章,终于慢慢看懂了如何快速的建立一个Electron app demo,前一篇文章不是使用官方快速搭建的,而且还出了小问题,所以去撸了一遍quick-sta ...

  8. [实战]MVC5+EF6+MySql企业网盘实战(8)——文件下载、删除

    写在前面 上篇文章通过iframe实现了文件的无刷新上传.这篇我们将实现文件的下载与删除. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySq ...

  9. Electron开发跨平台桌面程序入门教程

    最近一直在学习 Electron 开发桌面应用程序,在尝试了 java swing 和 FXjava 后,感叹还是 Electron 开发桌面应用上手最快.我会在这一篇文章中实现一个HelloWord ...

随机推荐

  1. vue项目中设置全局引入scss,使每个组件都可以使用变量

    在Vue项目中使用scss,如果写了一套完整的有变量的scss文件.那么就需要全局引入,这样在每个组件中使用. 可以在mian.js全局引入,下面是使用方法. 1: 安装node-sass.sass- ...

  2. WPF学习(二) - 绑定

    绑定,这个看起来很神奇的东西,于我这种喜欢刨根儿的人而言,理解起来非常困难.    WPF绑定的核心思想是:数据层属性值的改变,能反应给展示层,反之亦然,并且这个响应的过程能被分离出来. 传统Winf ...

  3. c++类模板初探

    #include <iostream> #include <string> using namespace std; // 你提交的代码将嵌入到这里 ; template &l ...

  4. How Javascript works (Javascript工作原理) (十) 使用 MutationObserver 监测 DOM 变化

    个人总结: 这篇文章介绍了几种监测DOM变化的方法,重点介绍的是一个新浏览器API叫做MutationObserver. 注意:不要和Vue.js种 Object.defineProperty() 的 ...

  5. C语言运行时数据结构

    段(Segment): 对象文件/可执行文件: SVr4 UNIX上被称为ELF(起初"Extensible Linker Format", 现在"Executable ...

  6. Adobe Flex迷你教程 —Flex4全屏显示

    应用场景 1.播放器 我们经常看视频的时候,需要全屏显示,(在flex中这个视频初始化的时候是嵌入到html的iframe中). 2.监控 如下图所示,大多时候我们的监控用的是flex,而树形菜单和标 ...

  7. The Karplus-Strong Algorithm

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/48730857 Karplus-Stro ...

  8. ETL-informatica进阶资料整理

    名称 资源 说明 Informatica全球客户支持网站 https://network.informatica.com/ Informatica全球客户支持网站Network,其前身为MySuppo ...

  9. XML快速注释

    eclipse中编辑java或C/C++,python文件时,注释的快捷键均为 "CTRL + / ",编辑xml文件时,该快捷键无效. eclipse XML 注释:CTRL + ...

  10. 洛谷 P3147 [USACO16OPEN]262144

    P3147 [USACO16OPEN]262144 题目描述 Bessie likes downloading games to play on her cell phone, even though ...