美文网首页js css html
Node实现断点续传

Node实现断点续传

作者: 赵客缦胡缨v吴钩霜雪明 | 来源:发表于2023-03-01 13:47 被阅读0次

断点续传,顾名思义就是文件上传/下载过程中,遇到不可抗力,比如网络中断,服务器异常,或者其他原因导致操作中断;再次操作时,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。

这样就避免了文件重复上传/下载,浪费服务器空间使用,节约服务器资源,而且速度更快,更高效。

断点续传-分片上传

断点续传上传将要上传的文件分成若干个分片(Part)分别上传,所有分片都上传完成后,将所有分片合并成完整的文件,完成整个文件的上传。

利用MD5 , MD5 是文件的唯一标识,可以利用文件的 MD5 查询文件的上传状态;

设计思路:

将文件切成多个小的文件;

将切片并行上传;

所有切片上传完成后,服务器端进行切片合成;

当分片上传失败,可以在重新上传时进行判断,只上传上次失败的部分实现断点续传;

当切片合成为完整的文件,通知客户端上传成功;

已经传到服务器的完整文件,则不需要重新上传到服务器,实现秒传功能;

/**
 * 切片上传
 *
 */
const PATH = require("path");
const FS = require("fs");
const mkDir = require("./ApiMkDir"); // 创建目录的封装方法

module.exports = function (req, res, config) {
  return new Promise((resolve, reject) => {
    // 获取时间
    // 生成储存目录名称
    let date = new Date();
    let path = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`;
    // 文件目录
    let savePath = PATH.resolve(config.root, "assets/" + path);
    let cachePath = PATH.resolve(config.root, "cacheUploads");
    // 判断目录是否存在,不存在创建目录
    if (!FS.existsSync(savePath)) mkDir(savePath);
    if (!FS.existsSync(cachePath)) mkDir(cachePath);
    // 获取分片数据
    let { index, total, md5 } = req.fields;
    // 临时文件路径
    let TmpFileName = cachePath + "/" + req.files["file"].name;
    // 存储文件路径
    let FileName = savePath + "/" + req.files["file"].name;
    // 当前文件传输进度管理
    let TmpFileNameMange = cachePath + "/" + req.files["file"].name + ".txt";
    TmpFileName = PATH.normalize(TmpFileName);
    TmpFileNameMange = PATH.normalize(TmpFileNameMange);
    FileName = PATH.normalize(FileName);
    // 是否第一次传输
    if (FS.existsSync(FileName)) FS.unlinkSync(FileName);
    if (FS.existsSync(TmpFileNameMange)) {
      let test = FS.readFileSync(TmpFileNameMange, { encoding: "utf-8" });
      if (test) {
        let { i, md5: md } = JSON.parse(test);
        if (md === md5 && Number(i) > Number(index)) {
          resolve(i);
          return;
        }
      }
    }
    // 获取上传的文件buffer
    let buffer = FS.readFileSync(req.files["file"].path);
    // 写入临时文件
    if (FS.appendFileSync(TmpFileName, buffer)) reject();
    // 传输完成,移动到保存目录
    // 写入保存文件
    FS.writeFileSync(
      TmpFileNameMange,
      JSON.stringify({ i: index, total, md5 })
    );
    if (index === total) {
      FS.renameSync(TmpFileName, FileName);
      FS.unlinkSync(TmpFileNameMange);
      resolve();
      return;
    }
    resolve(index);
  });
};
 async submit() {
      let file = this.$refs.file.files[0];
      this.upload(file);
    },
    async upload(file, index = 0) {
      // 获取文件大小
      let fileSize = file.size;
      // 每个块的大小
      let chunkSize = 1024 * 1024 * 0.0005;
      // 共多少块
      let chunkNum = Math.ceil(fileSize / chunkSize);
      // 定义formData对象
      let formData = new FormData();
      // 定义结束位置;
      let end = index + 1;
      // 片段是否最后一片,如果不是最后一片,那么就是每片的位置
      if (end < chunkNum) end = end * chunkSize;
      // 如果是最后一片,结束位置等于文件最后的位置
      else end = fileSize;
      // 获取单个切片
      let chunData = file.slice(index * chunkSize, end);
      // 储存单个切片
      formData.append("file", chunData, file.name);
      formData.append("index", index + 1); //第几片
      formData.append("total", chunkNum); //第几片
      formData.append('md5',md5(file))
      let { data } = await axios({
        url: "http://127.0.0.1:3000/api/chunkUpload",
        method: "post",
        data: formData,
        headers: { token: "token" }
      });
      // 后台需要返回当前切片位置
      index = data.data - 0; 
      if (end < fileSize) this.upload(file, index);
    }

断点续传-下载

要实现断点续传,需要进行以下步骤:获取文件的大小和已经下载的部分。

在下载文件之前,需要确定要下载的文件的大小以及已经下载的部分。

可以使用 Node.js 中的 fs.statSync() 方法获取文件的大小,或者通过发起 HEAD 请求来获取文件的大小。

对于已经下载的部分,需要记录在先前下载时下载的字节数。

使用 HTTP Range 头。 HTTP Range 头允许客户端请求服务器仅发送文件的某一部分。

使用 Range 头可以让我们下载文件的指定部分,从而实现断点续传。

我们可以使用 HTTP 模块中的 request() 方法来发送 HTTP 请求,并在请求头中包含 Range 头。

将下载的部分写入文件,可以使用 Node.js 中的 fs.createWriteStream() 方法创建一个写入文件的流,并在下载过程中将数据写入文件。

设计思路:

第一步:检查文件是否存在,如果存在,就设置 Range 头,这样服务器就只会返回文件的未下载部分。

第二步:创建一个写入文件的流,并发送 HTTP 请求。

第三步:如果服务器返回状态码 206(Partial Content),则表示可以进行断点续传;否则,就从头开始下载整个文件。

第四步:最后,将响应流写入文件。

代码示例:

const fs = require('fs');
const http = require('http');

const fileUrl = 'http://example.com/file.zip';
const filePath = './file.zip';

// 获取文件大小和已下载的部分
let options = {};
if (fs.existsSync(filePath)) {
  const stat = fs.statSync(filePath);
  const range = `bytes=${stat.size}-`;
  options.headers = { 'Range': range };
}

// 发送请求并写入文件
const file = fs.createWriteStream(filePath, { flags: 'a' });
const request = http.get(fileUrl, options, (response) => {
  // 如果服务器返回 206(Partial Content),则表示可以断点续传
  if (response.statusCode === 206) {
    console.log('继续下载');
  } else if (response.statusCode === 200) {
    console.log('从头开始下载');
  } else {
    console.log(`无法下载,错误代码:${response.statusCode}`);
    return;
  }
  response.pipe(file);
});

// 处理错误
request.on('error', (err) => {
  console.error(`下载出错:${err.message}`);
});

// 下载完成
file.on('finish', () => {
  console.log('下载完成');
});

// 关闭文件流
file.on('close', () => {
  console.log('文件流已关闭');
});

相关文章

  • Okhttp多线程断点续传

    目录 1、断点续传相关定义2、多线程下载实现方案 1、断点续传相关定义 1.1、断点续传: 记录上次下载的位置,下...

  • IOS 断点续传原理浅析(第一篇)

    断点续传概述: 断点续传就是从文件上次中断的地方开始重新下载或上传数据,当下载大文件的时候,如果没有实现断点续传功...

  • iOS-16 断点续传 下载

    断点续传概述: 断点续传就是从文件上次中断的地方开始重新下载或上传数据,当下载大文件的时候,如果没有实现断点续传功...

  • Android断点下载小结

    前言 断点续传是一个很传统的话题;现在但凡包含下载功能的软件,大部分都会有断点续传的功能;因此对于断点续传的实现,...

  • rsync - 断点续传

    总结   其实,rsync本身就支持断点续传,加上--partial的作用是能实现单个文件内的断点续传(当文件比较...

  • node静态服务器断点续传实现

    当用户从静态文件服务器上获取诸如歌曲这样的流媒体文件时,如果网络连接断开,重连后未做处理,就需要重新下载这个文件。...

  • NSURLSessionDataTask实现文件下载(实现离线断

    NSURLSessionDataTask实现文件下载(实现离线断点续传下载) 编程思路实现细节:根据文件名拼接沙盒...

  • iOS开发!知识点!汇总

    一.断点续传相关 1.iOS模仿断点机制上传文件实现方法 2.iOS多任务断点续传之"框架"封装 二.有关硬件支持...

  • Android开发记录-基于okhttp的断点续传

    需求基于okhttp实现文件断点续传 一、okhttp 省略okhttp依赖和使用方式。 二、实现 1、inter...

  • Android面试题整理

    断点续传的实现 从字面上理解,所谓断点续传就是从停止的地方重新下载。 断点:线程停止的位置。 续传:从停止的位置重...

网友评论

    本文标题:Node实现断点续传

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