咱们一般会使用File System模块对文件进行读取,以下:git
fs.readFile('./test.txt', (error, buffer) => {
if (error) {
console.error(error)
} else {
// 读取文件成功
res.write(buffer)
}
})
复制代码
这样操做简单有效,但这也存在一些问题:github
相比File System,Stream流读取文件是读一份,发一份,Stream流的写入操做也有一样特色,所以能够解决File System在上面提到的2个问题。浏览器
接下来实现一个简单的流,将1.txt文件的内容写入到2.txt中:bash
示例代码:/lesson24/stream.js网络
const fs = require('fs')
// 建立一个可读流。
const readStream = fs.createReadStream('./1.txt')
// 建立一个可写流。
const writeStream = fs.createWriteStream('./2.txt')
// 将可读流读取的数据,经过管道pipe推送到写入流中,便可将1.txt的内容,写入到2.txt中。
readStream.pipe(writeStream)
// 读取出现错误时会触发error事件。
readStream.on('error', (error) => {
console.error(error)
})
// 写入完成时,触发finish事件。
writeStream.on('finish', () => {
console.log('finish')
})
复制代码
可使用Zlib模块,配合Stream流,实现文件压缩功能,以下:less
示例代码:/lesson24/gzip.jside
const fs = require('fs')
// 引入zlib模块,用于实现压缩功能
const zlib = require('zlib')
// 建立一个可读流。
const readStream = fs.createReadStream('./google.jpg')
// 建立一个可写流。
const writeStream = fs.createWriteStream('./google.jpg.gz')
// 建立一个Gzip对象,用于将文件压缩成.gz文件
const gzip = zlib.createGzip()
// 将可读流读取的数据,先经过管道pipe推送到gzip中,再推送到写入流中。
// 也就是先将可读流的数据压缩,再推送到可写流中。
readStream.pipe(gzip).pipe(writeStream)
// 读取出现错误时会触发error事件。
readStream.on('error', (error) => {
console.error(error)
})
// 写入完成时,触发finish事件。
writeStream.on('finish', () => {
console.log('finish')
})
复制代码
学习了流,咱们就能够更加高效地将文件传输到前台:学习
示例代码:/lesson24/server.js网站
const http = require('http')
const zlib = require('zlib')
const url = require('url')
const fs = require('fs')
const server = http.createServer((req, res) => {
const {
pathname
} = url.parse(req.url, true)
// 建立一个可读流。
const readStream = fs.createReadStream(`./${pathname}`)
// 建立一个Gzip对象,用于将文件压缩成.gz文件
const gzip = zlib.createGzip()
// 将读取的内容,在经过管道推送到res中,该方法不通过压缩
readStream.pipe(res)
// 处理可读流报错,防止请求不存在的文件
readStream.on('error', (error) => {
console.error(error);
res.writeHead(404)
res.write('Not Found')
res.end()
})
})
server.listen(8080)
复制代码
但能够看到,在这个例子里,虽然实现了使用流传输文件,但并无用到gzip压缩,在传输时仍是更多地消耗网络资源,接下来能够引入gzip压缩。ui
但此时若是只是简单的用readStream.pipe(gzip).pipe(res)
传输文件,浏览器在访问时没法直接打开,而是会触发文件下载。
这是由于未设置请求头属性content-encoding的值,致使浏览器没法识别用gzip压缩过的文件,这就须要修改请求头res.setHeader('content-encoding', 'gzip')
,让浏览器能够识别。
这样浏览器就能够正常打开文件了,但若浏览器访问的是不存在的文件,浏览器会报错“没法访问此网站”,这是由于请求头属性content-encoding已被设置为gzip,但服务端传给浏览器的是Not Found字符串,浏览器没法识别。
此时可使用fs.stat方法,先检查文件是否存在,若不存在则返回Not Found,若存在则继续传输。
示例代码:/lesson24/server_gzip.js
const http = require('http')
const zlib = require('zlib')
const url = require('url')
const fs = require('fs')
const server = http.createServer((req, res) => {
const {
pathname
} = url.parse(req.url, true)
// 文件的相对路径
const filepath = `./${pathname}`
// 检查文件是否存在
fs.stat(filepath, (error, stat) => {
if (error) {
console.error(error);
res.setHeader('content-encoding', 'identity')
res.writeHead(404)
res.write('Not Found')
res.end()
} else {
// 建立一个可读流。
const readStream = fs.createReadStream(filepath)
// 建立一个Gzip对象,用于将文件压缩成.gz文件
const gzip = zlib.createGzip()
// 向浏览器发送通过gzip压缩的文件,设置响应头,不然浏览器没法识别,会自动进行下载。
res.setHeader('content-encoding', 'gzip')
// 将读取的内容,经过gzip压缩以后,在经过管道推送到res中,因为res继承自Stream流,所以也能够接收管道的推送。
readStream.pipe(gzip).pipe(res)
// 处理可读流报错,防止文件中途被删除或出错,致使报错。
readStream.on('error', (error) => {
console.error(error);
res.setHeader('content-encoding', 'identity')
res.writeHead(404)
res.write('Not Found')
res.end()
})
}
})
})
server.listen(8080)
复制代码