咱們一般會使用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)
複製代碼