下面是详细讲解“Node中文件断点续传原理和方法总结”的完整攻略。
简介
文件断点续传是指在文件下载或上传过程中,若因网络等原因中断,再次续传时可以从断点处接着传输,而不必重新开始。在Node.js中,我们可以使用HTTP断点续传头来实现文件断点续传。
HTTP断点续传头
HTTP断点续传头是指在HTTP请求头中设置Range
和If-Range
字段,从而实现文件断点续传。
Range字段
Range
字段指定了请求的字节范围。如果服务器支持请求的范围(即该文件可以被分割为几个部分进行下载或上传),则响应头会返回其支持的范围,并在响应头中添加Accept-Ranges: bytes
。若服务器不支持请求的范围,则不会添加该头部。
Range字段的格式如下:
Range: bytes=<start>-<end>
其中,start
表示起始字节位置,end
表示终止字节位置,在服务端会使用这两个值计算出需要返回的数据量。
If-Range字段
If-Range
字段用于判断服务器上的文件是否发生改变。如果文件没有发生改变,则请求头中的Range
字段生效,服务器只返回请求的字节范围。如果文件发生改变,则不返回请求的字节范围,而是返回整个文件的内容。
If-Range字段的格式如下:
If-Range: <etag>
其中,etag
是文件的一个标识符。服务器在响应头中返回文件的etag
,客户端在后续请求中使用If-Range
字段判断文件是否发生改变。
实现
实现文件断点续传的主要步骤如下:
- 创建HTTP服务,当接收到下载请求时,读取文件的内容并返回给客户端。
- 确定客户端请求的字节范围,设置响应头
Content-Range
返回需要的字节范围。 - 设置
If-Range
字段校验文件是否发生改变。
以下是一个简单的示例:
const http = require('http')
const fs = require('fs')
http.createServer((req, res) => {
const { url } = req
const filename = 'path/to/file'
// 获取文件状态
const { size } = fs.statSync(filename)
// 判断请求是否包含 Range 头
const range = req.headers.range
if (range) {
// 解析 Range 头
const [start, end] = range.replace('bytes=', '').split('-').map(e => parseInt(e))
// 设置 Content-Range 头
res.setHeader('Content-Range', `bytes ${start}-${end || size - 1}/${size}`)
res.statusCode = 206
// 创建可读流
const stream = fs.createReadStream(filename, { start, end })
// 向响应中写入流数据
stream.pipe(res)
} else {
// 设置 Content-Length 头
res.setHeader('Content-Length', size)
// 创建可读流
const stream = fs.createReadStream(filename)
// 向响应中写入流数据
stream.pipe(res)
}
}).listen(3000, () => {
console.log('server is listening on port 3000')
})
在这个示例中,当收到HTTP请求后,我们首先获取要下载的文件的大小,并检查是否有Range
请求头。如果有,则解析出start
和end
的参数值,并设置响应头Content-Range
,响应状态码为206
。如果没有,则设置响应头Content-Length
。
然后,我们创建一个可读流stream
,并使用pipe
方法将其写入响应流res
。当写入流的字节数达到指定的end
字节位置时,流将停止写入。
示例
下面介绍两个示例。
上传文件
这个示例演示了如何上传文件并支持断点续传。我们假设上传的文件名为example.jpg
,上传到/uploads
目录下,以下是示例代码:
const http = require('http')
const fs = require('fs')
http.createServer((req, res) => {
const { url, method } = req
if (url === '/upload' && method === 'POST') {
const filename = 'uploads/example.jpg'
// 确定内容范围,通过 Range 头获取起始位置
const range = req.headers.range || null
const [start] = range ? range.replace('bytes=', '').split('-').map(e => parseInt(e)) : [0]
// 创建可写流
const fileStream = fs.createWriteStream(filename, { flags: start === 0 ? 'w' : 'a', start })
// 返回响应码 201
res.statusCode = 201
// 监听数据
req.on('data', (chunk) => fileStream.write(chunk))
// 监听结束事件
req.on('end', () => {
fileStream.end()
// 返回文件信息
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ filename, size: fs.statSync(filename).size }))
})
} else {
// 返回404
res.statusCode = 404
res.end()
}
}).listen(3000, () => {
console.log('server is listening on port 3000')
})
在示例中,我们首先检查是否收到了上传请求,如果收到则确定文件名和位置,以及文件的内容范围。如果收到的范围是不为空的,则从头文件中提取出起始点。接下来,我们使用fs.createWriteStream
创建一个可写流,从指定位置开始写入文件。如果起始点为0,则使用w
标志打开文件。如果起始点不为0,则使用a
标志续写该文件。
我们在请求结束后关闭可写流,然后向客户端返回文件的状态信息。
下载文件
这个示例演示了如何下载文件并支持断点续传。我们假设下载的文件名为example.jpg
,存储在/uploads
目录下,以下是示例代码:
const http = require('http')
const fs = require('fs')
http.createServer((req, res) => {
const { url } = req
const filename = 'uploads/example.jpg'
if (url === '/download') {
// 确定内容范围,通过 Range 头获取起始位置
const range = req.headers.range || null
const [start, end] = range ? range.replace('bytes=', '').split('-').map(e => parseInt(e)) : [0, undefined]
// 获取文件信息
const { size } = fs.statSync(filename)
// 设置内容范围
const rangeStart = start === 0 ? start : start < size ? start : 0
const rangeEnd = end ? end < size ? end : size - 1 : size - 1
const contentLength = rangeEnd - rangeStart + 1
// 设置响应头
res.setHeader('Accept-Ranges', 'bytes')
res.setHeader('Content-Type', 'image/jpeg')
res.setHeader('Content-Length', contentLength)
// 如果是断点下载,设置 Content-Range 头
if (range) {
res.statusCode = 206
res.setHeader('Content-Range', `bytes ${rangeStart}-${rangeEnd}/${size}`)
}
// 创建可读流
const readStream = fs.createReadStream(filename, { start: rangeStart, end: rangeEnd })
// 所有数据处理完成后,关闭响应
readStream.pipe(res).on('close', () => {
res.end()
})
} else {
// 返回404
res.statusCode = 404
res.end()
}
}).listen(3000, () => {
console.log('server is listening on port 3000')
})
在示例中,我们首先检查是否收到了下载请求。如果是,则确定文件名和位置,以及客户端请求的范围。接下来,我们获取文件信息,例如文件大小,并根据请求的范围计算出需要下载的字节数。
然后,我们设置响应头,包括Accept-Ranges
、Content-Type
和Content-Length
。如果请求头包含有效的范围,我们还需要设置Content-Range
和响应码为206
。
最后,我们创建一个可读流,提供请求范围内的数据,并将其作为响应数据管道返回给客户端。同时,我们将监听数据管道中close
事件,当数据处理完成后关闭响应,保证数据正确返回。
以上就是关于Node中文件断点续传原理和方法总结的攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Node中文件断点续传原理和方法总结 - Python技术站