Nodejs断点续传服务示例

Nodejs断点续传服务示例

2017-11-21 / 0 评论 / 189 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2021年10月27日,已超过912天没有更新,若内容或图片失效,请留言反馈。

断点续传:从文件已经下载的地方开始继续下载。

在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。

一般断点下载时才用到 Range 和 Content-Range 实体头

代码示例

http.createServer(function(request, response) {
    var pathname = url.parse(request.url).pathname;
    var realpath = path.join(__dirname, '..', path.normalize(pathname.replace(/\.\./g, "")));
    var ext = path.extname(realpath);
    ext = ext ? ext.slice(1) : "unknown";
    var contentType = mime[ext].contentType;
    fs.exists(realpath, function(exists) {
      if (!exists) {
        response.writeHead(404, {
          'Content-Type': 'text/plain'
        });
        response.write("This request URL " + pathname + "was not found on this server");
        response.end();
      } else {
        response.setHeader("Content-Type", contentType);
        var stats = fs.statSync(realpath);
        if (request.headers["range"]) {
          var range = parseRange(request.headers["range"], stats.size);
          if (range) {
            response.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stats.size);
            response.setHeader("Content-Length", (range.end - range.start + 1));
            var stream = fs.createReadStream(realpath, {
              "start": range.start,
              "end": range.end
            });
            response.writeHead('206', "Partial Content");
            stream.pipe(response);
          } else {
            response.removeHeader("Content-Length");
            response.writeHead(416, "Request Range Not Satisfiable");
            response.end();
          }
        } else {
          var stream = fs.createReadStream(realpath);
          response.writeHead('200', "Partial Content");
          stream.pipe(response);
        }
      }
    });
  }).listen(8080)

parseRange 函数

function parseRange(str, size) {
  if (str.indexOf(",") != -1) {
    return;
  }
  if (str.indexOf("=") != -1) {
    var pos = str.indexOf("=")
    var str = str.substr(6, str.length)
  }
  var range = str.split("-");
  var start = parseInt(range[0], 10)
  var end = parseInt(range[1], 10) || size - 1
    // Case: -100
  if (isNaN(start)) {
    start = size - end;
    end = size - 1;
    // Case: 100-
  } else if (isNaN(end)) {
    end = size - 1;
  }
  // Invalid
  if (isNaN(start) || isNaN(end) || start > end || end > size) {
    return;
  }
  return {
    start: start,
    end: end
  };
};

mime

{
  "css": {
    "contentType": "text/css"
  },
  "gif": {
    "contentType": "image/gif",
    "mediaType": "image"
  },
  "html": {
    "contentType": "text/html"
  },
  "ico": {
    "contentType": "image/x-icon",
    "mediaType": "image"
  },
  "jpeg": {
    "contentType": "image/jpeg",
    "mediaType": "image"
  },
  "jpg": {
    "contentType": "image/jpeg",
    "mediaType": "image"
  },
  "js": {
    "contentType": "text/javascript"
  },
  "json": {
    "contentType": "application/json"
  },
  "pdf": {
    "contentType": "application/pdf"
  },
  "png": {
    "contentType": "image/png",
    "mediaType": "image"
  },
  "svg": {
    "contentType": "image/svg+xml",
    "mediaType": "image"
  },
  "swf": {
    "contentType": "application/x-shockwave-flash"
  },
  "tiff": {
    "contentType": "image/tiff"
  },
  "txt": {
    "contentType": "text/plain"
  },
  "wav": {
    "contentType": "audio/x-wav"
  },
  "wma": {
    "contentType": "audio/x-ms-wma"
  },
  "wmv": {
    "contentType": "video/x-ms-wmv"
  },
  "xml": {
    "contentType": "text/xml"
  },
  "mp4": {
    "contentType": "video/mp4",
    "mediaType": "video"
  },
  "m3u8": {
    "contentType": "application/x-mpegURL",
    "mediaType": "video"
  },
  "unkown": {
    "contentType": "application/octet-stream"
  }
}

0

评论 (0)

取消