信息爆炸的时代,获取到信息的门槛大大降低,但同时也加大了我们理解信息的难度。针对某一个问题,网上会有关于这个问题的好多篇博客或文章。但是翻过一遍后会发现,这些文章中去掉全文都是代码、什么解释也没有的,复制粘贴的,使用各种不必要的框架解决问题的,描述逻辑混乱的,再去掉排版不好看着不舒服的,之后也没几篇能看的了。每个人似乎都在想要提供一个解决方案,但是描述问题的时候能否按逻辑来,一步步走,代码加点注释啥的。我自己写东西或者解决问题的时候喜欢先理清需求,划分步骤,然后一步一步来。比如说,NodeJs 如何实现上传图片到服务器。
1. 任务需求
很常见的一个场景。用户注册页面更换头像后,将图片上传到服务器存储并返回图片在服务器的地址。这里需要注意的一个问题是因为要获取服务器返回的图片地址,所以要异步提交表单。
2. 步骤划分
- 前端js 代码中判断表单中的图片发生变化时,异步提交表单。
- 服务器端js 接收到请求,存储图片并返回图片url。
- 前端js 处理返回请求获取图片url。
3. 代码实现
3.1 前端html
<img class="photo photo-img" src="">
<form id="img_form" action="" method="POST" enctype="multipart/form-data">
<input name="img" type="file" id="img-input" οnchange="changeImg()" style="display: none"/>
</form>
3.2 前端js
因为异步提交表单要用到form.ajaxSubmit,所以需要在html 的头文件中引入jquery.form.js,下载地址:
http://malsup.github.io/jquery.form.js
function changeImg() {
// 图片更改后异步提交表单上传服务器,返回url
var form = $("#img_form");
var options = {
url: domain + '/upload/img', //上传文件的路径
type:'post',
success:function(data){
data = JSON.parse(data);
if(data.code == 0) {
imgUrl = data.data.url;
} else {
alert('上传图片出错!');
}
}
};
form.ajaxSubmit(options);
// 在前端显示上传的图片
var file = $('.photo').find('input')[0].files[0];
var reader = new FileReader();
reader.onload = function(e){
var imgFile = e.target.result;
$('.photo-img').attr('src',imgFile);
$('.photo-img').attr('style','display:block');
}
reader.readAsDataURL(file);
}
3.3 后端js
文件上传需要用到multer 包,所以需要在app.js 中引入,app.js 写法:
"use strict"
var express = require('express');
var multer = require("multer");
var app = express();
var bodyParse = require('body-parser');
// 设置图片存储路径
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads');
},
filename: function(req, file, cb) {
cb(null, `${Date.now()}-${file.originalname}`)
}
})
// 添加配置文件到muler对象。
var upload = multer({ storage: storage });
var imgBaseUrl = '../'
// bodyParse 用来解析post数据
app.use(bodyParse.urlencoded({extended:false}));
app.use(express.static('public'));
// 解决跨域问题
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200); /让options请求快速返回/
}
else {
next();
}
});
// 文件上传请求处理,upload.array 支持多文件上传,第二个参数是上传文件数目
app.post('/upload/img', upload.array('img', 2), function (req, res) {
// 读取上传的图片信息
var files = req.files;
// 设置返回结果
var result = {};
if(!files[0]) {
result.code = 1;
result.errMsg = '上传失败';
} else {
result.code = 0;
result.data = {
url: files[0].path
}
result.errMsg = '上传成功';
}
res.end(JSON.stringify(result));
});
// 监听3000端口
var server=app.listen(3000, '0.0.0.0', function () {
console.log('listening at =====> http://0.0.0.0:3000......');
}) ;