完全基于node的web应用

完全基于node的web应用

 

 

事实上通常“正确的方式”一般都不简单。

这其中涉及了nodejs中服务端JavaScript,函数式编程,阻塞与非阻塞,回调,事件,内部和外部模块等问题,但是函数式编程,阻塞与非阻塞,回调,事件这些编程思想是通用的,值得学习。
Node入门

用例

  • 用户可以通过浏览器使用我们的应用。

  • 当用户请求http://domain/start时,可以看到一个欢迎页面,页面上有一个文件上传的表单。

  • 用户可以选择一个图片并提交表单,随后文件将被上传到http://domain/upload,该页面完成上传后会把图片显示在页面上。

模块

  • 提供一个web页面,需要http服务器。

  • 需要一个路由处理URL请求,对应到请求处理程序(requet handle)。

  • 路由处理POST数据,把数据封装好后传递给请求处理程序。

  • 最终的请求处理程序。

  • 返回一个对应视图给浏览器。

  • 上传功能。

基本http服务器

  • server.js

    1. var http = require ("http"); 
    2. http.createServer(function (request,response){ 
    3. response.writeHead(200,{"Content-Type":"text/plain"}); 
    4. response.write("hello world"); 
    5. response.end(); 
    6. }).listen(8888); 

基于事件驱动回调

  • server.js

  1. var http = require("http"); 
  2.  
  3. //这是一个事件回调函数 
  4. function onRequest(request, response) { 
  5. console.log("Request received."); 
  6. response.writeHead(200, {"Content-Type": "text/plain"}); 
  7. response.write("Hello World"); 
  8. response.end(); 
  9. } 
  10. //一旦由请求事件,onRequest函数被调用 
  11. http.createServer(onRequest).listen(8888); 
  12.  
  13. console.log("Server has started."); 

模块化server

  • server.js

    1. var http = require("http"); 
    2.  
    3. function start() { 
    4. function onRequest(request, response) { 
    5. console.log("Request received."); 
    6. response.writeHead(200, {"Content-Type": "text/plain"}); 
    7. response.write("Hello World"); 
    8. response.end(); 
    9. } 
    10.  
    11. http.createServer(onRequest).listen(8888); 
    12. console.log("Server has started."); 
    13. } 
    14. //导出函数 
    15. exports.start = start; 

路由模块,依赖注入到service

  • router.js

    1. function route(pathname) { 
    2. console.log("About to route a request for " + pathname); 
    3. } 
    4.  
    5. exports.route = route; 
  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route) { 
    5. function onRequest(request, response) { 
    6. var pathname = url.parse(request.url).pathname; 
    7. console.log("Request for " + pathname + " received."); 
    8.  
    9. route(pathname);//依赖注入 
    10.  
    11. response.writeHead(200, {"Content-Type": "text/plain"}); 
    12. response.write("Hello World"); 
    13. response.end(); 
    14. } 
    15.  
    16. http.createServer(onRequest).listen(8888); 
    17. console.log("Server has started."); 
    18. } 
    19.  
    20. exports.start = start; 
  • index.js

    1.  
    2. var server = require("./server"); 
    3. var router = require("./router"); 
    4.  
    5. server.start(router.route);//依赖注入 

行为驱动处理函数,函数式编程

  • requestHandlers.js

    1. //行为实现 
    2. function start() { 
    3. console.log("Request handler 'start' was called."); 
    4. return "Hello Start"; 
    5. } 
    6.  
    7. function upload() { 
    8. console.log("Request handler 'upload' was called."); 
    9. return "Hello Upload"; 
    10. } 
    11.  
    12. exports.start = start; 
    13. exports.upload = upload; 
  • index.js

    1. var server = require("./server"); 
    2. var router = require("./router"); 
    3. var requestHandlers = require("./requestHandlers"); 
    4.  
    5. var handle = {}//根据路由执行行为定义 
    6. handle["/"] = requestHandlers.start; 
    7. handle["/start"] = requestHandlers.start; 
    8. handle["/upload"] = requestHandlers.upload; 
    9.  
    10. server.start(router.route, handle); 
  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route, handle) { 
    5. function onRequest(request, response) { 
    6. var pathname = url.parse(request.url).pathname; 
    7. console.log("Request for " + pathname + " received."); 
    8.  
    9. response.writeHead(200, {"Content-Type": "text/plain"}); 
    10. var content = route(handle, pathname) //路由行为分配 
    11. response.write(content); 
    12. response.end(); 
    13. } 
    14. http.createServer(onRequest).listen(8888); 
    15. console.log("Server has started."); 
    16. } 
    17.  
    18. exports.start = start; 
  • router.js

    1. function route(handle, pathname) { 
    2. console.log("About to route a request for " + pathname); 
    3. //根据路由分配行为 
    4. if (typeof handle[pathname] === 'function') { 
    5. return handle[pathname](); 
    6. } else { 
    7. console.log("No request handler found for " + pathname); 
    8. return "404 Not found"; 
    9. } 
    10. } 
    11.  
    12. exports.route = route; 

阻塞与非阻塞

nodeJS它通过事件轮询(event loop)来实现并行操作,所以尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。
要用非阻塞操作,我们需要使用回调。
回调就是:你继续处理你的事情,我(Node.js线程)先不等你了,我继续去处理你后面的代码,请你提供一个callbackFunction(),等你处理完之后我会去调用该回调函数的,谢谢!

阻塞的请求

  • requestHandlers.js

  1. function start() { 
  2. console.log("Request handler 'start' was called."); 
  3.  
  4. function sleep(milliSeconds) { 
  5. var startTime = new Date().getTime(); 
  6. while (new Date().getTime() < startTime + milliSeconds); 
  7. } 
  8. sleep(10000); 
  9. return "Hello Start"; 
  10. } 
  11.  
  12. function upload() { 
  13. console.log("Request handler 'upload' was called."); 
  14. return "Hello Upload"; 
  15. } 
  16. exports.start = start; 
  17. exports.upload = upload; 
  18.  

处理POST请求

  • requestHandlers.js

    1. function start(response) { 
    2. console.log("Request handler 'start' was called."); 
    3.  
    4. var body = '<html>'+ 
    5. '<head>'+ 
    6. '<meta http-equiv="Content-Type" content="text/html; '+ 
    7. 'charset=UTF-8" />'+ 
    8. '</head>'+ 
    9. '<body>'+ 
    10. '<form action="/upload" method="post">'+ 
    11. '<textarea name="text" rows="20" cols="60"></textarea>'+ 
    12. '<input type="submit" value="Submit text" />'+ 
    13. '</form>'+ 
    14. '</body>'+ 
    15. '</html>'; 
    16.  
    17. response.writeHead(200, {"Content-Type": "text/html"}); 
    18. response.write(body); 
    19. response.end(); 
    20. } 
    21.  
    22. function upload(response) { 
    23. console.log("Request handler 'upload' was called."); 
    24. response.writeHead(200, {"Content-Type": "text/plain"}); 
    25. response.write("Hello Upload"); 
    26. response.end(); 
    27. } 
    28.  
    29. exports.start = start; 
    30. exports.upload = upload; 

request监听器

  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route, handle) { 
    5. function onRequest(request, response) { 
    6. var postData = "";//传输的数据 
    7. var pathname = url.parse(request.url).pathname; 
    8. console.log("Request for " + pathname + " received."); 
    9.  
    10. request.setEncoding("utf8"); 
    11. //监听数据传输 
    12. request.addListener("data", function(postDataChunk) { 
    13. postData += postDataChunk; 
    14. console.log("Received POST data chunk '"+ 
    15. postDataChunk + "'."); 
    16. }); 
    17. //传输结束一次处理 
    18. request.addListener("end", function() { 
    19. route(handle, pathname, response, postData); 
    20. }); 
    21.  
    22. } 
    23.  
    24. http.createServer(onRequest).listen(8888); 
    25. console.log("Server has started."); 
    26. } 
    27.  
    28. exports.start = start; 

数据传输给路由

  • router.js

    1. function route(handle, pathname, response, postData) { 
    2. console.log("About to route a request for " + pathname); 
    3. if (typeof handle[pathname] === 'function') { 
    4. handle[pathname](response, postData); 
    5. } else { 
    6. console.log("No request handler found for " + pathname); 
    7. response.writeHead(404, {"Content-Type": "text/plain"}); 
    8. response.write("404 Not found"); 
    9. response.end(); 
    10. } 
    11. } 
    12.  
    13. exports.route = route; 

响应请求

  • requestHandlers.js

    1. var querystring = require("querystring"); 
    2.  
    3. function start(response, postData) { 
    4. console.log("Request handler 'start' was called."); 
    5.  
    6. var body = '<html>'+ 
    7. '<head>'+ 
    8. '<meta http-equiv="Content-Type" content="text/html; '+ 
    9. 'charset=UTF-8" />'+ 
    10. '</head>'+ 
    11. '<body>'+ 
    12. '<form action="/upload" method="post">'+ 
    13. '<textarea name="text" rows="20" cols="60"></textarea>'+ 
    14. '<input type="submit" value="Submit text" />'+ 
    15. '</form>'+ 
    16. '</body>'+ 
    17. '</html>'; 
    18.  
    19. response.writeHead(200, {"Content-Type": "text/html"}); 
    20. response.write(body); 
    21. response.end(); 
    22. } 
    23. function upload(response, postData) { 
    24. console.log("Request handler 'upload' was called."); 
    25. response.writeHead(200, {"Content-Type": "text/plain"}); 
    26. response.write("You've sent the text: "+ 
    27. querystring.parse(postData).text); 
    28. response.end(); 
    29. } 
    30.  
    31. exports.start = start; 
    32. exports.upload = upload; 

使用外部模块上传文件

node-formidable模块 安装

npm insatll formidable
npm install formidable -g express -d

node-formidable官方的例子

var formidable = require('formidable'),
    http = require('http'),
    util = require('util');

http.createServer(function(req, res) {
  if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
    // parse a file upload
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, fields, files) {
      res.writeHead(200, {'content-type': 'text/plain'});
      res.write('received upload:\n\n');
      res.end(util.inspect({fields: fields, files: files}));
    });
    return;
  }

  // show a file upload form
  res.writeHead(200, {'content-type': 'text/html'});
  res.end(
    '<form action="/upload" enctype="multipart/form-data" '+
    'method="post">'+
    '<input type="text" name="title"><br>'+
    '<input type="file" name="upload" multiple="multiple"><br>'+
    '<input type="submit" value="Upload">'+
    '</form>'
  );
}).listen(8888);

读取图片数据

  • requestHandlers.js

    1. var querystring = require("querystring"), 
    2. fs = require("fs"); 
    3.  
    4. function start(response, postData) { 
    5. console.log("Request handler 'start' was called."); 
    6.  
    7. var body = '<html>'+ 
    8. '<head>'+ 
    9. '<meta http-equiv="Content-Type" '+ 
    10. 'content="text/html; charset=UTF-8" />'+ 
    11. '</head>'+ 
    12. '<body>'+ 
    13. '<form action="/upload" method="post">'+ 
    14. '<textarea name="text" rows="20" cols="60"></textarea>'+ 
    15. '<input type="submit" value="Submit text" />'+ 
    16. '</form>'+ 
    17. '</body>'+ 
    18. '</html>'; 
    19.  
    20. response.writeHead(200, {"Content-Type": "text/html"}); 
    21. response.write(body); 
    22. response.end(); 
    23. } 
    24.  
    25. function upload(response, postData) { 
    26. console.log("Request handler 'upload' was called."); 
    27. response.writeHead(200, {"Content-Type": "text/plain"}); 
    28. response.write("You've sent the text: "+ 
    29. querystring.parse(postData).text); 
    30. response.end(); 
    31. } 
    32.  
    33. function show(response, postData) { 
    34. console.log("Request handler 'show' was called."); 
    35. fs.readFile("/tmp/test.png", "binary", function(error, file) { 
    36. if(error) { 
    37. response.writeHead(500, {"Content-Type": "text/plain"}); 
    38. response.write(error + "\n"); 
    39. response.end(); 
    40. } else { 
    41. response.writeHead(200, {"Content-Type": "image/png"}); 
    42. response.write(file, "binary"); 
    43. response.end(); 
    44. } 
    45. }); 
    46. } 
    47.  
    48. exports.start = start; 
    49. exports.upload = upload; 
    50. exports.show = show; 
  • index.js

    1. var server = require("./server"); 
    2. var router = require("./router"); 
    3. var requestHandlers = require("./requestHandlers"); 
    4.  
    5. var handle = {} 
    6. handle["/"] = requestHandlers.start; 
    7. handle["/start"] = requestHandlers.start; 
    8. handle["/upload"] = requestHandlers.upload; 
    9. handle["/show"] = requestHandlers.show; 
    10.  
    11. server.start(router.route, handle); 

上传图片

  • requestHandlers.js

    1. var querystring = require("querystring"), 
    2. fs = require("fs"), 
    3. formidable = require("formidable"); 
    4.  
    5. function start(response) { 
    6. console.log("Request handler 'start' was called."); 
    7.  
    8. var body = '<html>'+ 
    9. '<head>'+ 
    10. '<meta http-equiv="Content-Type" content="text/html; '+ 
    11. 'charset=UTF-8" />'+ 
    12. '</head>'+ 
    13. '<body>'+ 
    14. '<form action="/upload" enctype="multipart/form-data" '+ 
    15. 'method="post">'+ 
    16. '<input type="file" name="upload" multiple="multiple">'+ 
    17. '<input type="submit" value="Upload file" />'+ 
    18. '</form>'+ 
    19. '</body>'+ 
    20. '</html>'; 
    21.  
    22. response.writeHead(200, {"Content-Type": "text/html"}); 
    23. response.write(body); 
    24. response.end(); 
    25. } 
    26.  
    27. function upload(response, request) { 
    28. console.log("Request handler 'upload' was called."); 
    29.  
    30. var form = new formidable.IncomingForm(); 
    31. console.log("about to parse"); 
    32. form.parse(request, function(error, fields, files) { 
    33. console.log("parsing done"); 
    34. fs.renameSync(files.upload.path, "/tmp/test.png"); 
    35. response.writeHead(200, {"Content-Type": "text/html"}); 
    36. response.write("received image:<br/>"); 
    37. response.write("<img src='/show' />"); 
    38. response.end(); 
    39. }); 
    40. } 
    41.  
    42. function show(response) { 
    43. console.log("Request handler 'show' was called."); 
    44. fs.readFile("/tmp/test.png", "binary", function(error, file) { 
    45. if(error) { 
    46. response.writeHead(500, {"Content-Type": "text/plain"}); 
    47. response.write(error + "\n"); 
    48. response.end(); 
    49. } else { 
    50. response.writeHead(200, {"Content-Type": "image/png"}); 
    51. response.write(file, "binary"); 
    52. response.end(); 
    53. } 
    54. }); 
    55. } 
    56.  
    57. exports.start = start; 
    58. exports.upload = upload; 
    59. exports.show = show; 
  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route, handle) { 
    5. function onRequest(request, response) { 
    6. var pathname = url.parse(request.url).pathname; 
    7. console.log("Request for " + pathname + " received."); 
    8. route(handle, pathname, response, request); 
    9. } 
    10.  
    11. http.createServer(onRequest).listen(8888); 
    12. console.log("Server has started."); 
    13. } 
    14.  
    15. exports.start = start; 

fs模块的问题 文件路径问题

  1. function upload(response,request) { 
  2. console.log("Request handler 'upload' was called"); 
  3. // response.writeHead(200, { 
  4. // "Content-Type": "text/plain" 
  5. // }); 
  6. // response.write("you are sent: " + querystring.parse(postData).text); 
  7. // response.end(); 
  8. // response.write("hello upload"); 
  9. //return "hello upload"; 
  10. // 
  11. //文件上传 
  12. var form =new formidable.IncomingForm(); 
  13. console.log("start to parse"); 
  14. form.uploadDir = "tem"; 
  15. form.parse(request,function(error,fields,files){ 
  16. console.log("parsing done"); 
  17. try{ 
  18. fs.renameSync(files.upload.path,form.uploadDir+"/test.png");//主要修改了这个地方 
  19. }catch(e){ 
  20. console.log(e); 
  21. } 
  22.  
  23. response.writeHead(200,{"Content-Type":"text/html"}); 
  24. response.write("received image:<br/>"); 
  25. response.write("<img src='/show'>"); 
  26. response.end(); 
  27. }) 
  28. } 
posted @ 2017-01-08 16:44  明明.如月  阅读(1161)  评论(0)    收藏  举报