写在前面
这是一个比较简单的全栈Demo,写这个的动机主要是:当我查询网上一些nodejs资料时,很少有完整的传输文件的案例,大多是通过前端form 的action属性直接上传,所以萌生了写通过FromData上传图片的案例。功能实现之后做了一些前端展示。算是个比较不错的入门全栈项目。
##版本依赖
- “express”: “^4.16.2”,
- “file-type”: “^7.3.0”,
- “formidable”: “^1.1.1”,
- “multer”: “^1.3.0”,(可选)
- “read-chunk”: “^2.1.0”
这里的 read-chunk和filetype是为了读取文件后缀名,如果不想用库。
可以通过 split('.')[1] 拿到后缀名是一样的(好像用库代码还多了,捂脸(之前笔误写成了splice)splice是用于分割字符串,抱歉抱歉
直接在更目录下 npm install 再 node app.js就可以运行了。
前后端构架
前端是基于boostrap 的一个简单的表单上传和展示,并没有特别复杂的组件,这里不再阐述了。
###后端
- 先引入express,把服务跑起来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const express=require('express');
const path = require('path');
const app = express()
app.set('port',(process.env.PORT || 3080));
app.use(express.static('public'));
app.use('/uploads',express.static('uploads'));
app.get('/',function(req,res){
res.sendFile(path.join(__dirname,'views/index.html'));
});
app.listen(app.get('port'),function(){
console.log('Express started at port ' + app.get('port'));
})
这样就把之前放在views下的index.html引入进来了,定义的端口是3080。这样就可以访问了。
app.use是挂载中间件到特定的路径。path.join是解析相对路径,类似的path.resolve是绝对路径。
2.前端通过Ajax formData到后端1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$('#upload-photos').on('submit',function(){
event.preventDefault();
var formData=new FormData();
var files=$('#photos-input').get(0).files;
if(files.length===0){
alert('select at least One photo');
return false
}
if(files.length>3){
alert('You can upload no more than three photos');
return false
}
for(let i=0; i<files.length;i++){
formData.append('photo[]',files[i],files[i].name);
}
uploadFiles(formData);
})
以上代码可以拿到前端传过来的files,并通过new FormData()构建一个新的实例,用append方法传入文件数据。
event.preventDefault():
When the form is submitted, the default form submission action is prevented by calling the event.preventDefault() method, which prevents the form submission being sent to the server.
作用是阻止默认的表单上传。
可以在MDN看到append方法的文档。
拿到数据之后就可以通过如下代码post数据到nodejs了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25function uploadFiles(formData) {
$.ajax({
url:'/upload_photos',
method:'post',
data:formData,
processData: false,
contentType: false,
xhr: function(){
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress',function (oEvent) {
var progressBar = $('.progress-bar')
if(oEvent.lengthComputable){
var percent = (oEvent.loaded / oEvent.total) * 100;
progressBar.width(percent + '%');
}
if(percent===100){
progressBar.removeClass('active');
}
})
return xhr
}
}).done(handleSuccess).fail(function(xhr,status){
alert(status)
})
}
值得注意的是,这里的processData,contentType必须设置为false
xhr即XMLHttpRequest 可以在不刷新页面的情况下 更新数据,这里用来更新下面的进度条。
1 | app.post('/upload_photos',function(req,res){ |
前端Post的数据通过app.post进行接收。这里就到了formidable的用武之地了。
这里使用了链式调用,让formfidable响应(’file’ ‘error’ ‘end’)最后处理完成之后通过res.status.json()把所需要的数据传回后端,
一次完成的交互就差不多大功告成了!
删除过期文件
等上面都跑通之后,再来加点功能,拓展下get方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28app.get('/',function(req,res){
var filesPath = path.join(__dirname,'uploads/');
//read all the files of the uploads
fs.readdir(filesPath,function(err,files){
if(err){
console.log(err);
return
}
console.log('files:',files);
files.forEach((file)=>{
fs.stat(filesPath+file,(err,stats)=>{
if (err) {
console.log(err);
return;
}
var createAt = stats.ctime;
var days = Math.round((Date.now()-createAt)/(1000*60*60*24));
if (days > 1){
fs.unlink(filesPath+file,(err)=>{
console.log(err)
});
}
})
})
})
res.sendFile(path.join(__dirname,'views/index.html'));
});
这里用了 fs 的 readdir方法来读取文件夹下所有的文件 stat 读取文件的信息。这里ctime就是时间了。自定义一个最长时间,就可以用unlink方法删除了!
##后记
附上github地址
写到这里大概整体框架也就说完了,第一次写完整的技术博客压力山大。(技术很菜,还希望不要误导了别人。
有一些细节问题,知识点,在这里不方便阐述,我会单开页面来说,感谢各位的阅读!