获取http请求报文头部信息
利用nodejs中的 http.ServerRequest中获取1):
request.method
用来标识世野纤请求类型
request.headers
其中我们关心两个字段:
content-type
包含了表单类型和边界字符串(下面会介绍)信息。
content-length
post数据的长度
关于content-type
get请求的headers中没有content-type这个字段
post 的 content-type 有两种
application/x-www-form-urlencoded
这种就是一般的文本表单用post传地数据,只要将得到的脊亩data用querystring解析下就可以了
multipart/form-data
文件表单的传输,也是本文介绍的重点
获取POST数据
前面已经说过,post数据的传输是可能分包的,因此必然是异步的。post数据的接受过程如下:
var postData = ''request.addListener("data", function(postDataChunk) { // 有新的数据包到达就执行
postData += postDataChunk
console.log("Received POST data chunk '"+
postDataChunk + "'.")
})
request.addListener("end", function() { // 数据传输完毕
console.log('post data finish receiving: ' + postData )
})
注意,对于非文件post数据,上面以字符串接收是没问题的,但其实 postDataChunk 是一个 buffer 类型数据,在搜仿遇到二进制时,这样的接受方式存在问题。
nodejs的fs模块并没有提供一个copy的方法,但我们可以很容易的实现一个,比如:var source = fs.readFileSync('/path/to/source', {encoding: 'utf8'})
fs.writeFileSync('/path/to/dest', source)
这种方式是把文件内容全派旁部读入内存,然后再写入文件,对于小型的文本文件,这没有多大问题,比如grunt-file-copy就是这样实现的。但是对于体积较大的二进制文件,比如音频、视频文件,动辄几个GB大小,如果使用这种方法,很容易使内存“爆仓”。理想的方法应该是读一部分,写一部分,不管改做文件有多大,只要时间允许,总会处理完成,这里就需要用到流的概念。
如上面高大上的图片所示,我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。
Stream在nodejs中是EventEmitter的实现,并且有多种实现形式,例如:
http responses request
fs read write streams
zlib streams
tcp sockets
child process stdout and stderr
上面的文件复制可以简单实现一下:
var fs = require('fs')
var readStream = fs.createReadStream('/path/to/source')
var writeStream = fs.createWriteStream('/path/to/dest')
readStream.on('data', function(chunk) { // 当有数据流出时,写入数据
writeStream.write(chunk)
})
readStream.on('end', function() { // 当没有数据时,关闭数据流
writeStream.end()
})
上面的写法有一些问题,如果写入的速度跟不上读取的速度,有可能导致数据丢失。正常的情况应该是,写完一段,再读取下一段,如果没有写完的话,就让读取流先暂停,等写完再继续,于是代码可以修改为:
var fs = require('fs')
var readStream = fs.createReadStream('/path/to/source')
var writeStream = fs.createWriteStream('/path/to/dest')
readStream.on('data', function(chunk) { // 当有数据流出时,写入数据
if (writeStream.write(chunk) === false) { // 如果没有写完,暂停读取流
readStream.pause()
}
})
writeStream.on('drain', function() { // 写完后,继续读取
readStream.resume()
})
readStream.on('end', function() { // 当没有数据时,关闭数据流尘歼橡
writeStream.end()
})
或者使用更直接的pipe
// pipe自动调用了data,end等事件
fs.createReadStream('/path/to/source').pipe(fs.createWriteStream('/path/to/dest'))
下面是一个更加完整的复制文件的过程
var fs = require('fs'),
path = require('path'),
out = process.stdout
var filePath = '/Users/chen/Movies/Game.of.Thrones.S04E07.1080p.HDTV.x264-BATV.mkv'
var readStream = fs.createReadStream(filePath)
var writeStream = fs.createWriteStream('file.mkv')
var stat = fs.statSync(filePath)
var totalSize = stat.size
var passedLength = 0
var lastSize = 0
var startTime = Date.now()
readStream.on('data', function(chunk) {
passedLength += chunk.length
if (writeStream.write(chunk) === false) {
readStream.pause()
}
})
readStream.on('end', function() {
writeStream.end()
})
writeStream.on('drain', function() {
readStream.resume()
})
setTimeout(function show() {
var percent = Math.ceil((passedLength / totalSize) * 100)
var size = Math.ceil(passedLength / 1000000)
var diff = size - lastSize
lastSize = size
out.clearLine()
out.cursorTo(0)
out.write('已完成' + size + 'MB, ' + percent + '%, 速度:' + diff * 2 + 'MB/s')
if (passedLength <totalSize) {
setTimeout(show, 500)
} else {
var endTime = Date.now()
console.log()
console.log('共用时:' + (endTime - startTime) / 1000 + '秒。')
}
}, 500)
可以把上面的代码保存为copy.js试验一下
我们添加了一个递归的setTimeout(或者直接使用setInterval)来做一个旁观者,每500ms观察一次完成进度,并把已完成的大小、百分比和复制速度一并写到控制台上,当复制完成时,计算总的耗费时间,效果如图:
我们复制了一集1080p的权利的游戏第四季第7集,大概3.78G大小,由于使用了SSD,可以看到速度还是非常不错的,哈哈哈~
复制完成后,显示总花费时间
结合nodejs的readline, process.argv等模块,我们可以添加覆盖提示、强制覆盖、动态指定文件路径等完整的复制方法,有兴趣的可以实现一下,实现完成,可以
ln -s /path/to/copy.js /usr/local/bin/mycopy
这样就可以使用自己写的mycopy命令替代系统的cp命令
node中有个流的概念,stream。代表数据流动方向:向内流(可读流),向外流(可写流)。常用的流形式是文件,和tcp套接字。流是以快为单位发送数据,通过监听‘data’事件,每一次得到一块数据即进行一次 *** 作,当数据结束时,监听end事件,执行后续 *** 作。
可读流可以通过stream.pause()stream.resume()暂停恢复流。其中暂停流在写文件时会停止从该文件吵旦中读取数据,如果是TCP套接字则不会读取新的数据包,终止其他终端来的数据包。可写流是要从node向外输出的数据,可以是TCP连接也可以是文件。node不会在io *** 作上阻塞,写入的缓冲区如果被刷新,会发射drain事件,如果没有被刷新,数据将被存储在进程内存中。
TCP连接是双向的,因此既是可读流也是可写流。以node作为服务器,则HTTP的请求是可读流,响应是可写流。
通常在服务器端读取本地数据是快速的,即可读流是快速的。然而由于网络原因,很多响应即可写流是无法保证的,如果请求太多(在服务器早漏的读写范围内),而响应太慢,会导致服务器的可写流填满。此时需要避免慢客户端(前端响应慢)的情况。
[javascript] view plain copy
fs = require('fs')
require('http').createServer(function (req,res) {
var rs = fs.createReadStream('./1.txt')
rs.on('data', function(data) {
if(!res.write(data)){
rs.pause()
}
})
rs.on('drain', function(data) {
rs.resume()
})
rs.on('end', function(){
res.end()
})
}).listen(8080,function(){
console.log('正在监听...')
})
也可以使用stream.pipe()pipe接受可写流作为第一个参数,由传输源调用。
[javascript] view plain copy
fs = require('fs')
require('http').createServer(function (req,res) {
var rs 陆碰烂= fs.createReadStream('./1.txt')
rs.pipe(res)
}).listen(8080,function(){
console.log('正在监听...')
})
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)