node --- 游走在客户端和服务器间的http

本篇文章,讲述了一个很简单的上传图片(/start)到本地服务器,然后路由跳转到/upload.
写这个程序的目的是为了帮助理解HTTP的一些基本概念及node对于http api的实现以及程序的设计模式.
IP: 计算机之间的通信
TCP: 应用程序之间的通信
HTTP: 基于TCP实现的应用层协议,设计之初是为了提供一种HTML页面的发布和接收的方法,就是传输一些超文本(HyperText)的规则.

// 注:一些用到的基础,会在对应的代码前说明。下面先简单的介绍一下功能

当在浏览器中输入 http://localhost:8888/start 时,显示如下内容(有点丑,没写CSS…将就看吧):
在这里插入图片描述
选择文件:
在这里插入图片描述
在这里插入图片描述
点击Upload file
在这里插入图片描述

要解决如下几个问题:
1.服务器监听localhost,端口8888下,路径为/start的url
2.服务器处理该请求,并返回内容(通过http)
3.返回的内容对选择文件和Upload File做出反应

在此之前,需要做如下的准备: 根据依赖注入的思想,我们需要一个index.js文件来处理服务器和路由之间的关系.

index.js: 负责启动服务器,并向服务器传入路由和事件处理程序(事件处理程序可以参考《JavaScript高级程序设》P345)

// index.js
var server = require('./server');   // 服务器,监听浏览器发送的http请求
var route = require('./route');    // 路由,对不同路径进行不同的处理
var requestHandlers = require('./requestHandlers');    // 事件处理程序// 在index这一层,可以将requestHandlers做一些简单的处理
// 将事件处理程序对应到handle中,路径作为属性
var handle = {};
handle["/"] = requestHandlers.start;    // 如果值输入start就调用start的事件处理器
handle["/start"] = requestHandlers.start;   
handle["/uoload"] = requestHandlers.uoload;
handle["/show"] = requestHandlers.show; // 调用启动服务器..
server.start(route, handle);

server服务器: 监听端口8888(会根据浏览器的请求HTTP生成一个请求路径),将请求路径(request.url)、求报文(resquest)、响应报文(response)、以及事件处理程序交给路由层

// server.js
var http = require('http');function start(route, handle) {// 服务器的事件处理程序 onRequestfunction onRequest(request , response) {var pathname = request.url;// 因为会多请求一个/favicon.ico(具体原因百度) , 故过滤掉它if (pathname === '/favicon.ico' ) {// 过滤掉...} else {  // 其他路由// 将请求路径交给路由层route(handle, pathname, response, request);}}// 创建服务器http.createServer(onRequest).listen(8888);
}// 到处start方法,供其他js使用
exports.start = start;// 注:在JS中函数可以作为参数,可以作为返回值,还可以作为变量赋值.具体可以参考函数式编程

route层: 根据服务器传入的检查事件处理程序中是否含有该路径,如果有则交给请求处理层,否则返回404响应报文

// route.js
function route(handle, pathname, response, request) {if( typeof handle[path] === 'function') {handle[pathname](response, request);} else {response.writeHead(404,{'Content-Type' : "text/plain" })response.write('404 Not Found');  // 可以自定义html的404页面返回response.end();}
}     
exports.route = route;

请求处理层(requestHandlers): 对不同路劲的具体处理方式:start、upload、show。(后续可以根据需求拓展)

先分开来说明具体需求和实现,最后汇总
1.start: 显示一个上传的input框,和一个提交的按钮
// 注:上传的框引入了一个外部模块formidable 具体了解formidable

// start(这是讲解,后面汇总)
// start 会显示一个html页面,可以使用fs来写一个读取Html页面的函数getHTML
function getHTML(url) {let promise = new Promise(resolve, reject) {fs.readFile(url, (err, data) {if (!err) {resolve(data);} else {reject(err)}})return promise
}// 可以使用上面定义函数写start函数
function start(request, response) {let url = "./start.html"  // 最后会给出let html = getHTML(url);html.then((data) => {response.writeHead(200,{'Content-Type' : 'text/plain'});response.write(data);  // 可以接收buffer , stringresponse.end();}, (err) => {console.log(err);})
}

2.upload: 用于处理start页面传入的图片,将图片放入/temp文件夹下.重新命名为test.png,并将图片作为http响应报文的body部分传递给浏览器.浏览器根据img的src 访问找到图片显示出来.

function upload(request , response) {var form = new formidable.IncomingForm();// 这里涉及到一个cross-device问题.在结尾的参考文献中会给出form.uploadDir = "temp";form.parse(request, (err, fields, files) => {fs.renameSync(files.upload.path, "/temp/test.png");response.writeHead(200,{ 'Content-Type' : 'text/html'});response.write("received image:<br /> ");response.write("<img src='/show' />");response.end();})
}

3.show: 用于将本地的/temp/test.png显示在浏览器上.触发条件是<img src=’/show’ />.即触发了(/show路由,但是在请求url中还是localhost:8888/upload)

function show (request, response) {let url = "./temp/test.png";fs.readFile(url, 'binary', (err, file) => {if(err ) {response.writeHead(500, {'Content-Type' : 'text/plain'});response.write(err + '\n' );response.end();} else {response.writeHead(200, {'Content-Type' : 'image/jpg' });    // 告诉浏览器是个图片类型response.write(file, 'binary');response.end();}})
}

1+2+3 = requestHandlers.js:

// requestHandlers.js   (这样写的可维护性,拓展性更高)
var fs = require('fs'),formidable = require('formidable');// 根据url获取本地的html(buffer)
function getHTML(url) {let promise = new Promise((resolve, reject) => {fs.readFile(url, (err, data) => {if (!err) {resolve(data)} else {reject(data)}})})return promise;
}function start(response) {let url = './start.html';let html = getHTML(url);html.then((body) => {response.writeHead(200, { 'Content-Type': 'text/html' });response.write(body);response.end();}, (err) => {console.log(err);})
}function upload(response, request) {var form = new formidable.IncomingForm();form.uploadDir = 'temp';form.parse(request, (err, fields, files) => {fs.renameSync(files.upload.path, "./temp/test.png");response.writeHead(200, { "Content-Type": "text/html" });response.write("received image:<br />");response.write("<img src='/show' />");response.end();})
}function show(response) {let url = "./temp/test.png";fs.readFile(url, "binary", (err, file) => {if (err) {response.writeHead(500, { "Content-Type": "text/plain" });response.write(err + '\n');response.end();} else {response.writeHead(200, { "Content-Type": "image/jpg" });response.write(file, "binary");response.end();}})
}exports.start = start;
exports.upload = upload;
exports.show = show;

start.html

<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html" charset="utf-8">
</head><body><!-- 使用post方法将表单中的元素提交到相对路径下的upload中 --><form action="/upload" method="post" enctype="multipart/form-data"><input type="file" name="upload"><input type="submit" value="Upload file" /></form>
</body></html>

参考1: fs.renameSync报错的问题
参考2: Node入门

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis配置环境变量

直接上图不解释 redis安装路径&#xff0c;我的电脑右击属性 窗口R键&#xff0c;输入cmd回车&#xff0c;输入redis-server.exe 回车 再开一个命令窗口&#xff0c;窗口R键&#xff0c;输入cmd回车&#xff0c;输入 redis-cli.exe -h 127.0.0.1 -p 6379 回车 转载于:https:/…

对转义字符的思考

ASCII码 计算机存储文字时用的是二进制&#xff0c;ASCII码就是一张对照表&#xff0c;什么字符对应什么码&#xff0c;将二进制码存储下来0-127位表示基础的ASCII码0-31&#xff0c;和127表示非打印控制字符&#xff08;如换行、回车、响铃、文头、文尾&#xff09;32-126表示…

ES6-12 array/数值拓展、ArrayOf、ArrayFrom

要使用…须有迭代器接口 数组方法 构造器上的方法 Array.of()声明数组 替代new Array()的方式声明数组new Array()不传参数返回空数组&#xff0c;只传1个参数时&#xff0c;代表数组长度&#xff0c;内容用empty填充&#xff0c;传多个参数&#xff0c;则代表数组内容&…

React01

目录 React-day01 入门知识React介绍官网React开发环境初始化 SPA脚手架初始化项目&#xff08;方便&#xff0c;稳定&#xff09;*通过webpack进行初始化配置镜像地址开发工具配置元素渲染组件及组件状态函数定义组件(无状态)类定义组件(有状态)*组合组件Props属性*State状态*…

算法 --- 反转数组

几个注意点: 1.输出的时候,也要做数字超出处理 2.js中可以使用 str -0 将字符串类型转换成数字类型 ( 注意不是 0) 3.可以使用 num ‘’ 将数字类型转换成字符串类型 4.使用str.split(’’) 可以将字符串转换成数组 5.使用arr.join(’’) 可以将数组转换成字符串 6.JS中2的31次…

ES6-13 正则方法、修饰符yus、UTF_16编码方式

修饰符 m multiLine 对于str中含\n的情况g globali ignoreCase 元字符 反斜杠加转义 元字符含义简写\w匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’。word\W匹配非字母、数字、下划线。等价于 ‘[^A-Za-z0-9_]’。\s匹配任何空白字符&#xff0c;包括空格、制表符、换页…

cmd窗口快速定位到具体文件夹方法

在用Python进行机器实战时&#xff0c;打开cmd窗口后&#xff0c;总是到定位到kNN.py所在文件夹才能Python&#xff08;否则import kNN失败&#xff09;&#xff0c;每次都要输入地址非常麻烦 这里介绍一个cmd窗口快速定位到具体文件夹方法&#xff1a; 按住Shift键右击鼠标打开…

算法 --- 罗马数字转整数

解体思路: 1.写一个对象trans用于保存罗马和数字之间的映射关系 2.重点在于当数值小的出现在数值大的左边时,会减去该数,出现在右边时会加上该数,因此需要与后面的进行比较 3.在得到s时,首先给它转换成字符串,并在末位加一个0 /*** param {string} s* return {number}*/ var r…

正则 - 阮一峰

学习地址 一 正则实例方法 1. test 正则实例.test返回布尔值 var r /x/g; var s _x_x;r.lastIndex // 0 r.test(s) // truer.lastIndex // 2 r.test(s) // truer.lastIndex // 4 r.test(s) // false死循环&#xff0c;因为while循环的每次匹配条件都是一个新的正则表达式…

算法 --- 有效的括号

解题思路: 1.对括号分别赋值(左括号大于0,右括号小于0),方便后期比较 2.使用栈,对于大于0的字符串入栈,对于小于0的字符串,检查栈中是否有元素,若没有返回false,否则拿出栈顶的一个元素,和现在的元素进行比较 /** * param {string} s * return {boolean} */ var isValid func…

ES6-14 Unicode表示法、字符串方法、模板字符串

Unicode表示法 本身能正常识别的&#xff0c;加{}也能识别&#xff0c;花括号内的内容表示码点 parseInt(0061,16) // 十进制的97&#xff0c;97在ASCII码中对应a console.log(\u0061) // a console.log(\u{0061}) // a console.log(\u{61}) // a原型上方法 codePointAt(十进…

算法 --- 删除数组中重复项

解题思路: 如果输入的数组长度为1,则返回该数组否则(len>2),使用i记录当前待插入的位置,j记录下一个与nums[i]不相等的位置,leng为待返回数组的长度当nums[i] ! nums[j]时,把j位置的值nums[j]放在i1位置.同时i,j /*** param {number[]} nums* return {number}*/ var remov…

如何理解 Linux 中的 load averages

原文&#xff1a;https://mp.weixin.qq.com/s?src11&timestamp1533697106&ver1047&signaturepoqrJFfcNABv4biKKpa4mZdIW7No2Wo1F5sbZL7ggoVS2GqcSqwQQ8hMulAmezT*zL*klB-eE5BeMyNuyjuIH7YgkBAN25i6*ahhEpWyxqx6vPct-Vr7q7AU0YGe-F*l&new1 http://blog.scoutap…

Jsp+Servlet+MYSQL注册登录案例(界面难看,ε=(´ο`*)))唉)

注册登录界面尤为常见&#xff0c;我的界面尤为难看&#xff0c;勉为其难的写吧&#xff0c;前端不熟就是这样。。。 这个案例运用到了: 1.Jsp动态页面--->动态页面 2.Servlet逻辑判断后台---->实现界面与数据库/业务的连接&#xff0c;简而言之&#xff0c;起承转合。PS…

ES6-15 map与set

Promise、Proxy、Map、Set这些ES6新增的api无法用babel实现语法降级&#xff0c;需要使用到polyfill Set 成员是唯一的&#xff0c;不能重复有iterator接口&#xff0c;可迭代具有iterator接口的所有类型&#xff0c;都能作为new Set()的参数&#xff0c;如类数组、数组 con…

ES6-16 WeakMap与WeakSet、proxy与reflect

WeakMap/WeakSet 原型上不存在遍历方法(没有部署iterator接口)成员只能是对象垃圾回收机制不考虑对成员对象的应用 WeakSet/WeakMap 中的对象都是弱引用&#xff0c;即垃圾回收机制不考虑 WeakSet 对该对象的引用&#xff0c;也就是说&#xff0c;如果其他对象都不再引用该对象…

ES6-17 class与对象

class 模拟类的方式语法糖&#xff08;把以前的写法换了一个方式&#xff09; 类内部的方法是不可枚举的 ES5用Object.assign拓展的原型属性是可枚举的 function Point(x, y) {this.x x;this.y y; } // 这样定义的原型上方法eat\drink是可枚举的 Point.prototype Object…

算法 --- 二叉树的最大深度

思路: 1.二叉树的深度,等于Max(左子树最大深度,右子树最大深度) 1 2.节点不存在时,此时的深度为0 3.当节点存在,左右子树不存在时(此时为叶子节点) 返回1 /*** Definition for a binary tree node.* function TreeNode(val) {* this.val val;* this.left this.righ…

ES6-18/19 异步的开端-promise

ES6-18异步的开端-promise ES6-19 promise的使用方法和自定义promisify try catch只能捕获同步异常&#xff0c;不能捕获异步的 等待所有异步都执行完&#xff0c;打印结果&#xff0c;比较笨拙的方法&#xff0c;在每个异步操作加arr.length 3 && show(arr) Promis…

算法 --- 平衡二叉树

解题思路: 1.首先写一个返回深度的函数d 2.写一个遍历函数t 3.在t中首先判断,r是否为空(为空则此时就是平衡二叉树,返回true),然后判断是否为叶子节点(r.left null && r.right null)若是则返回true,最后判断,其左子树的深度与右子树的深度之差是否大于1.若是则返回fal…