1、创建node18容器
docker run --rm -it -v "D:/AIHUB_workSpace/USC-courses/EE547":/usr/src/app -p 3000:3000 node:18 /bin/bash
如果没有会自动下载node:18的image,然后自动进入container内部,退出会自动删除container,该image大小为990.67MB
注:之后使用npm install也在/usr/src/app下进行,如果在进入container时的默认根目录进行,可能会出现如Tracker "idealTree" already exists
的错误
2、查看版本
root@f2fd36a194ca:/# node -v
v18.9.0
root@f2fd36a194ca:/# npm -v
8.19.1
3、创建index.js文件
因为本地文件夹已经mount到了container内部,在windows该文件夹下直接添加文件便能在container内部看到
'use strict';
const http = require('http');
const server = http.createServer(function (req, res) {
if (req.url == '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('<html><body>Hello, node.js!</body></html>');
res.end();
}
});
server.listen(3000);
console.log('Server started, port 3000');
此时/usr/src/app下应该有index.js文件
到/usr/src/app启动该server
root@f2fd36a194ca:/usr/src/app# node index.js
Server started, port 3000
4、请求该端口
因为映射了端口3000到container内部的3000
因此可以直接打开浏览器
http://127.0.0.1:3000/
便能看到Hello, node.js!的返回结果
或者如果在Windows安装了curl,可以打开cmd,便能有如下结果
C:\Users\ASUS>curl http://127.0.0.1:3000/
<html><body>Hello, node.js!</body></html>
5、使用nodemon
当用该命令启动index.js时,如果nodemon检测到index.js文件有改变,会自动重新启动服务,便于快速开发
在container内部安装
npm install nodemon
如果这行安装不了,用
npm install -g nodemon
因为我们使用nodemon一般是在命令行使用,所以加上-g安装的nodemon才提供在命令行中使用该库的工具,否则就会提示找不到nodemon。如果我们只需要在程序中调用该包,则不用加-g即可
如果不小心关掉了cmd界面用该命令重新进入container
docker exec -it f2fd36a194cad7c294edbcbe04f5ae2bce003cb52c8d55b30f320ead6367af0c /bin/bash
现在用nodemon启动index.js
nodemon index.js
用http://127.0.0.1:3000/secret
访问服务器,会发现没有反应,也不会自动结束,而是处于等待状态
现在进化一下index.js如下,使之可以返回404
'use strict';
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
if (req.url == '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('<html><body>Hello, node.js!</body></html>');
res.end();
} else {
res.writeHead(404);
res.end();
}
});
server.listen(PORT);
console.log(`Server started, port ${PORT}`);
当我们在Windows端修改了该文件,nodemon会自动检测到文件改变,然后自动重新启动
现在访问不存在的路径会自动返回404
注:自动启动功能在Windows10上结合docker desktop,并没有测试成功,仍需手动重启,可能有一些兼容性原因; 一个StackOverflow上的解决方法是用nodemon --legacy-watch index.js启动,--legacy-watch会将nodemon听(listen)文件系统事件的行为变成轮询(poll),这样理论上会慢一点,但可以保证能够检测到文件的改变并自动重启
6、一个更好的index.js
'use strict';
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
let respBody = '';
let contentType = 'text/html';
let httpCode = 404;
try {
switch (req.method.toLowerCase()) {
case 'get':
// can add logs to see elements
console.error(req.url)
const urlPath = req.url.split('/');
if (req.url === '/') {
httpCode = 200;
respBody = 'Welcome home';
} else if (urlPath.length === 2 && urlPath[1] === 'greet') {
httpCode = 200;
respBody = '<html><body>Hello, node.js!</body></html>';
} else if (urlPath.length === 3 && urlPath[1] === 'greet' && urlPath[2].match(/^a-z+$/)) {
httpCode = 200;
respBody = `<html><body>Hello ${urlPath[2]}, node.js!</body></html>`;
}
break;
case 'post':
// add me
break;
}
} catch (err) {
respBody = `Error: ${err.message}`;
httpCode = 500;
}
res.writeHead(httpCode, { 'Content-Type': contentType });
res.write(respBody);
res.end();
});
server.listen(PORT);
console.log(`Server started, port ${PORT}`);
调试,-v可以输出完整的接受到的包信息
curl -v http://127.0.0.1:3000/greet
7、一个神奇的报错
[nodemon] app crashed - waiting for file changes before starting...
其实是因为我的js里有个语法错误,一个括号没有闭合而已,和文件有没有改变没有关系
8、使用url包,自动解析url,更易于应对更加复杂的url
'use strict';
const http = require('http');
const PORT = 3000;
// const qs = require('querystring');
const url = require('url');
const server = http.createServer((req, res) => {
let respBody = '';
let contentType = 'text/html';
let httpCode = 404;
console.error(req.url)
const reqUrl = url.parse(req.url, true);
console.error(reqUrl)
const urlPath = reqUrl.pathname.split('/');
console.error(urlPath)
if (urlPath.length === 2 && urlPath[1] === 'greet') {
httpCode = 200;
respBody = reqUrl.query.name ? `<html><body>Hello ${reqUrl.query.name}, node.js!</body></html>` :
'<html><body>Hello, node.js!</body></html>';
} else { }
res.writeHead(httpCode, { 'Content-Type': contentType });
res.write(respBody);
res.end();
})
server.listen(PORT);
// 这行代码将被执行,因为js的异步机制,而在python中则不会执行到这
console.log(`Server started, port ${PORT}`);
9、express中间件
我猜测express是基于html包做了封装,或者实现了类似html包的功能,也能提供服务,监听端口,并进行路由,以及其他更强大的功能
安装express,因为不需要在terminal使用,因此不用加-g(globally),直接locally安装即可
npm install express
index.js文件
'use strict';
const express = require('express');
const app = express();
app.get('/greet', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('<html><body>Hello, express!</body></html>');
res.end();
});
app.listen(3000);
可见这里我们没有处理除了/greet以外的路径,但express会自动处理此问题,返回404,不会像html包那样需要自己处理
10、静态内容
创建public文件夹,放入index.html
<html>
<body>
<p>
Hello, static page!
</p>
</body>
</html>
在index.js添加app.use那一句
'use strict';
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/greet', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('<html><body>Hello, express!</body></html>');
res.end();
});
app.listen(3000);
此时访问直接访问http://127.0.0.1:3000时就不会报错,会自动返回该index.html
添加图片:在public下创建image文件夹,并添加一张图
在index.html中使用
<html>
<body>
<p>
Hello, static page!
</p>
<div id="mascot">
<a href="https://www.google.com/search?q=silly+cats">
<img src="/image/deeplearning.jpg" />
</a>
</div>
</body>
</html>
11、url传参
'use strict';
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/greet', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
});
app.get('/greet/:firstName/:lastName', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.params.firstName} ${req.params.lastName}</body></html>`);
res.end();
});
app.listen(3000);
比如
http://127.0.0.1:3000/greet?first_name=1&last_name=xixi
得到Hello, 1 xixi
http://127.0.0.1:3000/greet/xixi/haha
得到Hello, xixi haha
12、npm install express-list-endpoints
查看定义的路由
index.js
'use strict';
const express = require('express');
const listEndpoints = require('express-list-endpoints');
const app = express();
console.log(listEndpoints(app));
app.use(express.static('public'));
app.get('/greet', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
});
app.get('/greet/:firstName/:lastName', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.params.firstName} ${req.params.lastName}</body></html>`);
res.end();
});
console.log(listEndpoints(app));
app.listen(3000);
得到结果,可见定义后输出了我们定义的路由
[ { path: '', methods: [], middlewares: [] } ]
[
{ path: '/greet', methods: [ 'GET' ], middlewares: [ 'anonymous' ] },
{
path: '/greet/:firstName/:lastName',
methods: [ 'GET' ],
middlewares: [ 'anonymous' ]
}
]
13、uuid
一个debug小技巧,用nodemon启动index.js如果有bug很难看出问题,而换成node启动则会直接报错提示,便于调试
安装uuid
npm install uuid
index.js文件
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/greet', (req, res) => {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
});
app.listen(3000);
也可以把greet中的语句分开写,但需要加入next(),不然不会执行下一个greet,而是直接结束该函数的执行
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/greet', (req, res, next) => {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
});
app.get('/greet', (req, res, next) => {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
});
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
});
app.listen(3000);
也可以定义到一个get里面,但next还是不能省
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
app.get('/greet',
(req, res, next) => {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
},
(req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
},
(req, res, next) => {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
);
app.listen(3000);
还可以单独写成函数,便于阅读
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.get('/greet',
preResponse,
(req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
},
postResponse
);
app.listen(3000);
将preResponse和postResponse绑定到所有的请求上(但访问http://127.0.0.1:3000不会触发这两个函数)
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
});
app.get('/hello', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello World</body></html>`);
res.end();
next();
});
app.use(postResponse);
app.listen(3000);
这样访问http://127.0.0.1:3000/hello或者http://127.0.0.1:3000/greet都会触发preResponse和postResponse
14、GET FORM
index.html文件
<html>
<body>
<p>
Hello, static page!
</p>
<div id="mascot">
<a href="https://www.google.com/search?q=silly+cats">
<img src="/image/deeplearning.jpg" />
</a>
</div>
<div id="form-container">
<form ACTION="/greet" METHOD="GET">
<label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
<br />
<label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
<br />
<input type="submit">
</form>
</div>
</body>
</html>
js文件
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
});
app.get('/hello', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello World</body></html>`);
res.end();
next();
});
app.use(postResponse);
app.listen(3000);
这样进入http://127.0.0.1:3000/时,会自动进入public文件夹下的index.html,然后会看到表格,输入姓和名,点击submit,会自动请求greet并将填写的内容作为参数传递,server收到后将得到的参数写到一个简单的html源码内并返回,此时浏览器客户端页面自动跳转,将看到自己填写的姓和名
GET 不够安全,内容全在url上(http://127.0.0.1:3000/greet?first_name=LUJIA&last_name=ZHONG),也很难传送文件
15、POST FORM
使用POST方法,表格内容都在HTTP的request body里;因此需要单独再实现一个app.post,而且参数无法从req.query拿到了
新的index.html
<html>
<body>
<p>
Hello, static page!
</p>
<div class="form-container">
<h4>GET</h4>
<form ACTION="/greet" METHOD="GET">
<label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
<br />
<label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
<br />
<input type="submit" value="Say Hello">
</form>
</div>
<div class="form-container">
<h4>POST</h4>
<form ACTION="/greet" METHOD="POST">
<label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
<br />
<label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
<br />
<input type="submit" value="Say Hello">
</form>
</div>
<div id="mascot">
<a href="https://www.google.com/search?q=silly+cats">
<img src="/image/deeplearning.jpg" />
</a>
</div>
</body>
</html>
可以用如下代码片段查看body(直接加入到之前的代码里即可
app.post('/greet', (req, res, next) => {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
console.error(body);
next();
});
});
可以在命令行界面得到first_name=Lujia&last_name=Zhong的输出
继续用如下代码进行调试,这里我们用了内置的querystring包
const qs = require('querystring');
app.post('/greet', (req, res, next) => {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
const query = qs.parse(body);
console.error(query);
next();
});
});
会得到 [Object: null prototype] { first_name: ‘Lujia’, last_name: ‘Zhong’ }
当然以上的调试代码会导致浏览器出现Cannot POST /greet,毕竟我们并没有写如何回应浏览器的代码
完整index.js代码,此时使用POST表格正常跳转,且可见参数并不在url上
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
});
app.get('/hello', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello World</body></html>`);
res.end();
next();
});
const qs = require('querystring');
app.post('/greet', (req, res, next) => {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
const query = qs.parse(body);
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${query.first_name} ${query.last_name}</body></html>`);
res.end();
next();
});
});
app.use(postResponse);
app.listen(3000);
16、body-parser解析body,而不用querystring
下载body-parser
npm install body-parser
index.js,唯一改的就是引入库和POST那几行
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
});
app.get('/hello', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello World</body></html>`);
res.end();
next();
});
const BodyParser = require('body-parser');
const bodyParser = BodyParser.urlencoded({ extended: false });
app.post('/greet', bodyParser, (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.body.first_name} ${req.body.last_name}</body></html>`);
res.end();
next();
});
app.use(postResponse);
app.listen(3000);
Html文件不变,可见bodyParser的传入使得能够直接从req访问到body以及其中的参数
17、FORM上传文件
index.html
<html>
<body>
<p>
Hello, static page!
</p>
<div class="form-container">
<h4>POST</h4>
<form ACTION="/greet" METHOD="POST" ENCTYPE="multipart/form-data">
<label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
<br />
<label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
<br />
<label for="avatar">Avatar:</label><input type="file" name="avatar_pic" id="avatar" />
<br />
<input type="submit" value="Say Hello">
</form>
</div>
</body>
</html>
其中ENCTYPE被指定成了"multipart/form-data",因为一般情况下,如果POST上传的东西有二进制的数据,比如这个图片,而不只是名字等字符串,或者当上传文件特别大时,我们选择使用multipart/form-data;
其他情况则用application/x-www-form-urlencoded,这个是默认值,不用自己指定
index.js,调试用
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
});
app.post('/greet', (req, res, next) => {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
console.log(body);
res.end('DEVELOPING');
next();
});
});
app.use(postResponse);
app.listen(3000);
浏览器正常收到返回的DEVELOPING,命令行查看输出的body
------WebKitFormBoundaryRBmt4TEHt22fuEXP
Content-Disposition: form-data; name="first_name"
Lujia
------WebKitFormBoundaryRBmt4TEHt22fuEXP
Content-Disposition: form-data; name="last_name"
Zhong
------WebKitFormBoundaryRBmt4TEHt22fuEXP
Content-Disposition: form-data; name="avatar_pic"; filename="948-bag-of-tokens.cpp"
Content-Type: text/plain
///
// a very clear solution
// Time Complexity: O(nlogn)
// Space Complexity: O(logn)
///
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int power) {
sort(tokens.begin(), tokens.end());
int res = 0, scores = 0, i = 0, j = tokens.size() - 1;
while (i <= j) {
if (power >= tokens[i]) {
power -= tokens[i++];
res = max(res, ++scores);
} else if (scores > 0) {
scores--;
power += tokens[j--];
} else {
break;
}
}
return res;
}
};
------WebKitFormBoundaryRBmt4TEHt22fuEXP--
Request complete -- path:/greet, status:200, id:35055e0d-8cc9-4ff2-8af2-9dedc25f4486
可以看到能够接收到文件的内容
继续调试,使用multer,并用其来保存接收到的文件
npm install multer
js代码
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.get('/greet', (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
res.end();
next();
});
const UPLOAD_DIR = `${__dirname}/upload`;
const Multer = require('multer');
const upload = Multer({ dest: UPLOAD_DIR });
app.post('/greet', upload.single('avatar_pic'), (req, res, next) => {
console.log(req.body, req.file);
res.end('DEVELOPING');
next();
});
app.use(postResponse);
app.listen(3000);
upload文件会被自动创建,并将上传的文件自动保存下来
输出如下,可以看到multer像querystring一样把参数解析出来了
[Object: null prototype] { first_name: 'Lujia', last_name: 'Zhong' } {
fieldname: 'avatar_pic',
originalname: '948-bag-of-tokens.cpp',
encoding: '7bit',
mimetype: 'text/plain',
destination: '/usr/src/app/upload',
filename: 'ac4c13e4064867d0e1dab05206943b39',
path: '/usr/src/app/upload/ac4c13e4064867d0e1dab05206943b39',
size: 847
}
18、客户端查看server本地文件
index.js
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
app.use(express.static('public'));
app.use('/assets', express.static('upload'));
app.use(postResponse);
app.listen(3000);
此时输入http://127.0.0.1:3000/assets/ac4c13e4064867d0e1dab05206943b39,最后这一串是某个文件的名字,随后该文件会被直接下载下来,而不是显示在浏览器
问题出在mime-type,浏览器使用mime-type来决定文件的处理方式,就像前面multer解析出来的:mimetype: ‘text/plain’,如果是图片则为image/jpeg
但因为之前multer保存下来的文件都没有类型后缀,所以我们需要改一改使用multer时的代码
index.js
'use strict';
const uuid = require('uuid');
const express = require('express');
const app = express();
app.use(express.static('public'));
app.use('/assets', express.static('upload'));
function preResponse(req, res, next) {
req.my_data = {
start_at: new Date(),
request_id: uuid.v4()
};
next();
}
function postResponse(req, res, next) {
console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
next();
}
app.use(preResponse);
const UPLOAD_DIR = `${__dirname}/upload`;
const Multer = require('multer');
const multerStorage = Multer.diskStorage({
destination: UPLOAD_DIR,
filename: function(req, file, cb) {
cb(null, uuid.v4() + '_' + file.originalname);
}
});
const upload = Multer({ storage: multerStorage });
app.post('/greet', upload.single('avatar_pic'), (req, res, next) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`<html><body>Hello, ${req.body.first_name} ${req.body.last_name}<br /><img src="/assets/${req.file.filename}" /></body></html>`);
res.end();
next();
});
app.use(postResponse);
app.listen(3000);
index.html和之前一样
<html>
<body>
<p>
Hello, static page!
</p>
<div class="form-container">
<h4>POST</h4>
<form ACTION="/greet" METHOD="POST" ENCTYPE="multipart/form-data">
<label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
<br />
<label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
<br />
<label for="avatar">Avatar:</label><input type="file" name="avatar_pic" id="avatar" />
<br />
<input type="submit" value="Say Hello">
</form>
</div>
</body>
</html>
提交后浏览器结果
当然如果提交的是文本,<img>标签是不能显示的,但现在不论提交什么都能在server端找到文件,且文件后缀和提交的保持一致
可以通过assets直接访问:
(1)http://127.0.0.1:3000/assets/4b8ec1ee-3a37-4a3c-ba5f-655bd6387d1c_948-bag-of-tokens.cpp
(2)对图片http://127.0.0.1:3000/assets/b22832bf-f3bd-4b84-afcf-94ea00f11d3b_rick.jpg