不使用框架建立簡單的Node.js web應用

不使用框架建立簡單的Node.js web應用

需求: 建立一個能夠上傳圖片的web應用。用戶能夠瀏覽應用,有一個文件上傳的表單。選擇圖片上傳,上傳完成以後能夠預覽圖片。上傳的圖片信息須要入庫(mysql)。

一個簡單的http服務

const http = require('http');
// 建立一個服務
const server  = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World');
});
// 服務錯誤監聽
server.on('clientError', (err, socket) => {
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
// 當前服務監聽的端口
server.listen(8888);
console.log("Server has started.");

涉及到的api:
http.createServer
res.writeHead
res.endhtml

當咱們在編寫node服務的時候,若是服務器有異常,那node服務就直接掛掉了。那如何保證咱們的node服務不會關閉,而且會自動重啓呢?
或者是咱們修改了代碼,如何實現修改的代碼直接生效而不用從新手動重啓node服務呢?node

npm install -g nodemon

在生產環境我通常使用pm2來管理node服務。mysql

自定義模塊

剛纔咱們定義了一個簡單的http服務。其中http是一個內置的模塊。那其實咱們的服務都會有一個入口文件,在這裏咱們定義爲index.js。那咱們如何像引用http內置模塊同樣,在index.js裏使用server.js呢?web

exports 與 module.exports 的區別:
exports是module.exports的簡寫。若是修改了exports的引用,也就是從新給exports賦值,則exports只是在當前文件級做用域內可用,而且exports的修改不會影響到module.exports的值。

server.jssql

const http = require('http');
function start() {
    const server  = http.createServer((req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Hello World');
    });
    server.on('clientError', (err, socket) => {
        socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
    });
    server.listen(8888);
    console.log("Server has started.");
}
// 經過給exports添加【屬性值】的方式導出方法
// 或者經過給module.exports添加屬性值
exports.start = start;

index.jsnpm

const server = require('./server');
server.start();
node index.js

路由處理

咱們知道,訪問一個web網站會有不一樣的頁面或者會調用不一樣的接口,那這些就對應這不一樣的請求路徑,同時這些請求還會對應不一樣的請求方法(GET, POST等)。那node如何針對這些不一樣的請求路徑去匹配對應的處理函數呢?api

爲了處理http請求的參數,咱們須要獲取到http請求的request,從中獲取到請求方式以及請求路徑。在這裏會依賴 url內置模塊。
首先創建routes文件夾存放路由處理瀏覽器

routes/index.js服務器

module.exports = (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World');
}

routes/upload.js框架

module.exports = (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('upload file');
}

新建route.js文件處理路由

function route(handle, pathname, req, res) {
  if (typeof handle[pathname] === 'function') {
    handle[pathname](req, res);
  } else {
    console.log("No request handler found for " + pathname);
    res.end('404 Not found');
  }
}
exports.route = route;

修改server.js

const http = require('http');
const url = require("url");
const routes = {};
function use(path, routeHandler) {
    routes[path] = routeHandler;
}
function start(route) {
    function handleRequest(req, res) {
        const pathname = url.parse(req.url).pathname;
        route(routes, pathname, req, res)
    }
    const server  = http.createServer(handleRequest);
    server.on('clientError', (err, socket) => {
        socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
    });
    server.listen(8888);
    console.log("Server has started.");
}
module.exports = {
    start,
    use
};

修改index.js

const server = require('./server');
const index = require('./routes/index');
const upload = require('./routes/upload');
const router = require('./route');
server.use('/', index);
server.use('/upload', upload);
server.start(router.route);

處理POST請求

咱們顯示一個文本區(textarea)供用戶輸入內容,而後經過POST請求提交給服務器。最後,服務器接受到請求,經過處理程序將輸入的內容展現到瀏覽器中。

給request註冊監聽事件

request.addListener("data", function(chunk) {
  // called when a new chunk of data was received
});

request.addListener("end", function() {
  // called when all chunks of data have been received
});

querystring登場,解析上傳數據

修改server.js裏的handleRequest方法

function handleRequest(req, res) {
    const pathname = url.parse(req.url).pathname;
    let postData = '';
    // 設置編碼
    req.setEncoding("utf8");
    req.addListener("data", function(postDataChunk) {
        postData += postDataChunk;
        console.log("Received POST data chunk '"+
        postDataChunk + "'.");
    });

    req.addListener("end", function() {
        route(routes, pathname, req, res, postData);
    });
}

route.js多加一個參數postData

function route(handle, pathname, req, res, postData) {
  if (typeof handle[pathname] === 'function') {
    handle[pathname](req, res, postData);
  } else {
    console.log("No request handler found for " + pathname);
    res.end('404 Not found');
  }
}
exports.route = route;

index.js

const exec = require("child_process").exec;
module.exports = (req, res, postData) => {
    // 能夠使用node模板
    const body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<form action="/upload" method="post">'+
    '<textarea name="text" rows="20" cols="60"></textarea>'+
    '<input type="submit" value="Submit text" />'+
    '</form>'+
    '</body>'+
    '</html>';

    res.end(body);
}

upload.js 修改

const querystring = require("querystring");
module.exports = (req, res, postData) => {
    const content = querystring.parse(postData).text;
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8'});
    res.end(content || 'Empty');
}

處理文件上傳

npm install formidable

server.js修改

function handleRequest(req, res) {
    const pathname = url.parse(req.url).pathname;
    route(routes, pathname, req, res);
}

routes/index.js

const exec = require("child_process").exec;
module.exports = (req, res) => {
    // 能夠使用node模板
    const body =  '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" '+
    'content="text/html; charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<form action="/upload" enctype="multipart/form-data" '+
    'method="post">'+
    '<input type="file" name="upload">'+
    '<input type="submit" value="Upload file" />'+
    '</form>'+
    '</body>'+
    '</html>';
    res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
    res.end(body);
}

showFile.js獲取磁盤圖片信息

const fs = require('fs');
const path = require('path');

function show(req, res) {
    const rootPath = path.resolve();
  fs.readFile(`${rootPath}/tmp/test.png`, "binary", function(error, file) {
    if(error) {
      res.writeHead(500, {"Content-Type": "text/plain"});
      res.write(error + "\n");
      res.end();
    } else {
      res.writeHead(200, {"Content-Type": "image/png"});
      res.write(file, "binary");
      res.end();
    }
  });
}

module.exports = show;

routes/uoload.js

const fs = require('fs');
const formidable = require("formidable");
const path = require('path');

module.exports = (req, res) => {
    const form = new formidable.IncomingForm();
    const rootPath = path.resolve();
    form.parse(req, function(error, fields, files) {
        console.log("parsing done");
        fs.renameSync(files.upload.path, `${rootPath}/tmp/test.png`);
        res.writeHead(200, {"Content-Type": "text/html;  charset=utf-8"});
        res.write("received image:<br/>");
        res.write("<img src='/show' />");
        res.end();
    });
}

同時在index.js中添加相應的路由。

處理數據存儲

本地安裝mysql服務
安裝mysql客戶端/使用命令行

npm install mysql
CREATE SCHEMA `nodestart` ;


CREATE TABLE `nodestart`.`file` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `filename` VARCHAR(300) NULL,
  `path` VARCHAR(500) NULL,
  `size` VARCHAR(45) NULL,
  `type` VARCHAR(45) NULL,
  `uploadtime` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

新建db.js文件

const mysql      = require('mysql');
const connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : '123456',
  database : 'nodestart'
});
function query(sql, params) {
    return new Promise((resolve, reject) => {
        connection.connect();
        connection.query(sql, params, function (error, results, fields) {
            if (error) reject(error);
            console.log('The solution is: ', results);
            resolve(fields);
        });
        connection.end();
    });
}
exports.query = query;

修改routes/upload.js

const fs = require('fs');
const formidable = require("formidable");
const path = require('path');
const db = require('../db');

module.exports = (req, res) => {
    const form = new formidable.IncomingForm();
    const rootPath = path.resolve();
    form.parse(req, function(error, fields, files) {
        console.log("parsing done");
        fs.renameSync(files.upload.path, `${rootPath}/tmp/test.png`);

        const fileObj = {
            size: files.upload.size,
            path: `${rootPath}/tmp/test.png`,
            filename: files.upload.name,
            type: files.upload.type,
            uploadtime: Date.now()
        }
        db.query('insert into nodestart.file set ?', fileObj).then((res) => {
            console.log(res)
        }).catch((err) => {
            console.log(err);
        })
        
        res.writeHead(200, {"Content-Type": "text/html;  charset=utf-8"});
        res.write("received image:<br/>");
        res.write("<img src='/show' />");
        res.end();
    });
}

待續:

如何使用Node建立一個代理服務器

Node 的適用場景

相關文章
相關標籤/搜索