需求: 建立一個能夠上傳圖片的web應用。用戶能夠瀏覽應用,有一個文件上傳的表單。選擇圖片上傳,上傳完成以後能夠預覽圖片。上傳的圖片信息須要入庫(mysql)。
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);
咱們顯示一個文本區(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(); }); }
待續: