Ninja 之路:试炼!求生演习——异步 I/O、http

时间:2023-11-14 16:58:14

鸣人火影之路的第一步,就是跟着卡卡西学习基本的忍术,让自己先在忍者的世界里生存下来,so,想要在 node 的世界里游刃有余,必须要掌握异步 I/O、http等核心技能。

ok,第一步先学会读懂需求

// NO1:
// Create a program that prints a list of files in a given directory,filtered by the extension of the files. You will be provided a directory name as the first argument to your program (e.g. '/path/to/dir/') and a file extension to filter by as the second argument. For example, if you get 'txt' as the second argument then you will need to filter the list to only files that end with .txt. Note that the second argument will not come prefixed with a '.'. The list of files should be printed to the console, one file per line. You must use asynchronous I/O. // tips
// The fs.readdir() method takes a pathname as its first argument and a callback as its second. The callback signature is: function callback (err, list) { /* ... */ } // where list is an array of filename strings. //You may also find node's path module helpful, particularly the extname method.
// The answer at the bottom
// NO2:
// This problem is the same as the previous but introduces the concept of modules. You will need to create two files to solve this.
// Create a program that prints a list of files in a given directory, filtered by the extension of the files. The first argument is the directory name and the second argument is the extension filter. Print the list of files (one file per line) to the console. You must use asynchronous I/O.
// You must write a module file to do most of the work. The module must export a single function that takes three arguments: the directory name, the filename extension string and a callback function, in that order. The filename extension argument must be the same as what was passed to your program. Don't turn it into a RegExp or prefix with "." or do anything except pass it to your module where you can do what you need to make your filter work.
// The callback function must be called using the idiomatic node(err, data) convention. This convention stipulates that unless there's an error, the first argument passed to the callback will be null, and the second will be your data. In this exercise, the data will be your filtered list of files, as an Array. If you receive an error, e.g. from your call to fs.readdir(), the callback must be called with the error, and only the error, as the first argument.
// You must not print directly to the console from your module file, only from your original program. // These four things are the contract that your module must follow.
// 1. Export a single function that takes exactly the arguments described.
// 2. Call the callback exactly once with an error or some data as described.
// 3. Don't change anything else, like global variables or stdout.
// 4. Handle all the errors that may occur and pass them to the callback. // The benefit of having a contract is that your module can be used by anyone who expects this contract. So your module could be used by anyone else who does learnyounode, or the verifier, and just work. // tips
// The answer at the bottom
// Create a new module by creating a new file that just contains your directory reading and filtering function. To define a single function export, you assign your function to the module.exports object, overwriting what is already there: module.exports = function (args) { /* ... */ } // Or you can use a named function and assign the name. // To use your new module in your original program file, use the require() call in the same way that you require('fs') to load the fs module. The only difference is that for local modules must be prefixed with './'. So,
// if your file is named mymodule.js then: var mymodule = require('./mymodule.js')

怎么样?是不是发现自己有时连需求都看不懂!没关系,下面就是中文了,不过最新的、高质量的新技术都是通过英语来传播的,所以最好还是坚持学英语,读英语技术书籍、博客!

// NO3
// 这次的问题需要使用到 http.get() 方法。然而,这一次,将有三个 URL 作为前三个命令行参数提供给你。
// 你需要收集每一个 URL 所返回的完整内容,然后将它们在终端(标准输出stdout)打印出来。这次你不需要打印出这些内容的长度,仅仅是内容本身即可(字符串形式);每个 URL 对应的内容为一行。重点是你必须按照这些URL在参数列表中的顺序将相应的内容排列打印出来才算完成。 // 提示
// 不要期待这三台服务器能好好的一起玩耍!他们可能不会把完整的响应的结果按照你希望的顺序返回给你,所以你不能天真地只是在收到响应后直接打印出来,因为这样做的话,他们的顺序可能会乱掉。
// 你需要去跟踪到底有多少 URL 完整地返回了他们的内容,然后用一个队列存储起来。一旦你拥有了所有的结果,你才可以把它们打印到终端。
// NO4:编写一个 TCP 时间服务器  

// 你的服务器应当监听一个端口,以获取一些 TCP 连接,这个端口会经由第一个命令行参数传递给你的程序。针对每一个 TCP 连接,你都必须写入当前的日期和24小时制的时间,如下格式:  

//     "YYYY-MM-DD hh:mm"  

//  然后紧接着是一个换行符。  

//  月份、日、小时和分钟必须用零填充成为固定的两位数:  

//     "2013-07-06 17:42"  

// 提示
// 这次练习中,我们将会创建一个 TCP 服务器。这里将不会涉及到任何 HTTP 的事情,因此我们只需使用 net 这个 Node 核心模块就可以了。它包含了所有的基础网络功能。 // net 模块拥有一个名叫 net.createServer() 的方法,它会接收一个回调函数。和 Node 中其他的回调函数不同,createServer() 所用的回调函数将会被调用多次。你的服务器每收到一个 TCP 连接,都会调用一次这个回调函数。这个回调函数有如下特征: function callback (socket) { /* ... */ } // net.createServer() 也会返回一个 TCP 服务器的实例,你必须调用 server.listen(portNumber) 来让你的服务器开始监听一个特定的端口。 // 一个典型的 Node TCP 服务器将会如下所示: var net = require('net')
var server = net.createServer(function (socket) {
// socket 处理逻辑
})
server.listen(8000) //socket 对象包含了很多关于各个连接的信息(meta-data),但是它也同时是一个 Node 双工流(duplex Stream),所以,它即可以读,也可以写。对这个练习来说,我们只需要对 socket 写数据和关闭它就可以了。 // 使用 socket.write(data) 可以写数据到 socket 中,用 socket.end() 可以关闭一个 socket。另外, .end() 方法也可以接收一个数据对象作为参数,因此,你可简单地使用 socket.end(data) 来完成写数据和关闭两个操作。
// NO5
// 编写一个 HTTP 文件 服务器,它用于将每次所请求的文件返回给客户端。 你的服务器需要监听所提供给你的第一个命令行参数所制定的端口。 同时,第二个会提供给你的程序的参数则是所需要响应的文本文件的位置。在这一题中,你必须使用 fs.createReadStream() 方法以 stream 的形式作出请求相应。
// 提示
//一个典型的 Node HTTP 服务器将会是这个样子: var http = require('http')
var server = http.createServer(function (req, res) {
// 处理请求的逻辑...
})
server.listen(8000) // fs 这个核心模块也含有一些用来处理文件的流式(stream) API。你可以使用 fs.createReadStream() 方法来为命令行参数指定的文件创建一个 stream。这个方法会返回一个 stream 对象,该对象可以使用类似 src.pipe(dst) 的语法把数据从 src流传输(pipe) 到 dst流中。通过这种形式,你可以轻松地把一个文件系统的 stream 和一个 HTTP 响应的 stream 连接起来。
// NO6
// 编写一个 HTTP 服务器,它只接受 POST 形式的请求,并且将 POST 请求主体(body)所带的字符转换成大写形式,然后返回给客户端。 // 提示
// through2-map 允许你创建一个 transform stream,它仅需要一个函数就能完成「接收一个数据块,处理完后返回这个数据块」 的功能 ,它的工作模式类似于 Array#map(),但是是针对 stream 的: var map = require('through2-map')
inStream.pipe(map(function (chunk) {
return chunk.toString().split('').reverse().join('')
})).pipe(outStream)
// NO7 HTTP JSON API 服务器 

// 编写一个 HTTP 服务器,每当接收到一个路径为 '/api/parsetime' 的 GET 请求的时候,响应一些 JSON 数据。我们期望请求会包含一个查询参数(query string),key 是 "iso",值是 ISO 格式的时间。  

  /api/parsetime?iso=2013-08-10T12:10:15.474Z  

//  所响应的 JSON 应该只包含三个属性:'hour','minute' 和 'second'。例如:  

     {
"hour": 14,
"minute": 23,
"second": 15
} // 然后增再加一个接口,路径为 '/api/unixtime',它可以接收相同的查询参数(query string),但是它的返回会包含一个属性:'unixtime',相应值是一个 UNIX 时间戳。例如: { "unixtime": 1376136615474 } //提示
// 你可以使用 Node 的核心模块 'url' 来处理 URL 和 查询参数(query string)。 url.parse(request.url, true) 方法会处理 request.url,它返回的对象中包含了一些很有帮助的属性,方便方便你处理 querystring。
// 你也应当争做 Web 世界的好公民,正确地为响应设置 Content-Type 属性: res.writeHead(200, { 'Content-Type': 'application/json' })

Pseudo code

// NO1
   var fs = require('fs')
var path = require('path') var folder = process.argv[2]
var ext = '.' + process.argv[3] fs.readdir(folder, function (err, files) {
if (err) return console.error(err)
files.forEach(function (file) {
if (path.extname(file) === ext) {
console.log(file)
}
})
})
// NO2
// ./solution/solution.js_ : var filterFn = require('./solution_filter.js')
var dir = process.argv[2]
var filterStr = process.argv[3] filterFn(dir, filterStr, function (err, list) {
if (err) {
return console.error('There was an error:', err)
} list.forEach(function (file) {
console.log(file)
})
}) // ./solution/solution_filter.js_ : var fs = require('fs')
var path = require('path') module.exports = function (dir, filterStr, callback) {
fs.readdir(dir, function (err, list) {
if (err) {
return callback(err)
} list = list.filter(function (file) {
return path.extname(file) === '.' + filterStr
}) callback(null, list)
})
}
// NO3
var http = require('http');
var i = 2,
aCon = []; function filterFn(aUrl) { if (i < 5) {
http.get(aUrl[i], function (res) {
res.setEncoding('utf8');
let rowData = ''; res.on('data', function (data) {
rowData += data;
}).on('end', ()=>{
i++;
aCon.push(rowData);
filterFn(aUrl);
}).on('error', (e) => {
console.log($(e.message));
});
});
} else {
aCon.forEach(function (val) {
console.log(val);
});
}
} filterFn(process.argv);
// NO4
var net = require('net') function zeroFill (i) {
return (i < 10 ? '0' : '') + i
} function now () {
var d = new Date()
return d.getFullYear() + '-' +
zeroFill(d.getMonth() + 1) + '-' +
zeroFill(d.getDate()) + ' ' +
zeroFill(d.getHours()) + ':' +
zeroFill(d.getMinutes())
} var server = net.createServer(function (socket) {
socket.end(now() + '\n')
}) server.listen(Number(process.argv[2]))
    // NO5
var http = require('http')
var fs = require('fs') var server = http.createServer(function (req, res) {
res.writeHead(200, { 'content-type': 'text/plain' }) fs.createReadStream(process.argv[3]).pipe(res)
}) server.listen(Number(process.argv[2]))
// NO6
const server = http.createServer((req, res) => {
if (req.method != 'POST') {
return res.end('send a POST\n');
} req.pipe(map(function (chunk) {
return chunk.toString().toUpperCase();
})).pipe(res);
}); server.listen(Number(process.argv[2]));
    // NO7
var http = require('http')
var url = require('url') function parsetime (time) {
return {
hour: time.getHours(),
minute: time.getMinutes(),
second: time.getSeconds()
}
} function unixtime (time) {
return { unixtime: time.getTime() }
} var server = http.createServer(function (req, res) {
var parsedUrl = url.parse(req.url, true)
var time = new Date(parsedUrl.query.iso)
var result if (/^\/api\/parsetime/.test(req.url)) {
result = parsetime(time)
} else if (/^\/api\/unixtime/.test(req.url)) {
result = unixtime(time)
} if (result) {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(result))
} else {
res.writeHead(404)
res.end()
}
})
server.listen(Number(process.argv[2]))

PS: nodeSchool