express实现图片上传

1,224 阅读4分钟

常见的前后端交互数据大部分都是json格式的数据,但是当涉及到图片、文件上传时,就需要用到form-data格式的数据,以前我们要把input标签的type属性设置为file格式,采用form提交的方式 需要把form的enctype属性设置为multipart/form-data,采用js提交的方式我们就需要手动new一个FormData对象,对其加工处理之后再提交。在javascript已经蔓延至多领域的今天, 我们将从一个最简单的例子入手,去实现一个完整的图片上传功能。

初始化express项目

首先,初始化一个express项目,这部分就不赘述,初始化项目见express文档,之后新建app.js启动文件和public文件夹用户存放静态页面。

//app.js

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

此时,运行node app.js,即可在localhost:3000浏览express项目。

编写前端静态页面

<!--public/upload.html-->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>请上传您的文件</title>
  </head>
  <body>
    <form action="/upload" enctype="multipart/form-data" method="post">
      <input type="file" name="upload" id="upload" multiple="multiple" value="" />
      <input type="submit" name="" id="" value="点击上传" />
    </form>
  </body>
</html>

此页面通过文件类型的input上传文件,并通过form提交formdata格式的数据。

<!--public/result.html-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>上传成功</title>
  </head>
  <body>
    <h1>上传成功</h1>
    <img src="/public/test.png"	/>
  </body>
</html>

此页面为上传文件后的反馈页面,预览上传的图片。

配置upload API

In Express 4, req.files is no longer available on the req object by default. To access uploaded files on the req.files object, use multipart-handling middleware like busboy, multer, formidable, multiparty, connect-multiparty, or pez.

由于在新版本express 4中,req.files不可用,所以我们需要借助npm包,这里我们借助的是formidable,请自行安装。

var express = require('express');
var app = express();
// 引入解析包
var formidable = require('formidable');
fs = require('fs');

app.get('/', function (req, res) {
  res.send('Hello World!');
});
// 设置静态文件目录
app.use('/public', express.static('public'));

app.post('/upload', function(req,res){
  var form = new formidable.IncomingForm();
  console.log('about to parse');
  form.parse(req, function(error, fields, files){
    console.log('parse done')
    console.log(files.upload.path);
    // 读取文件流并写入到public/test.png
    fs.writeFileSync('public/test.png', fs.readFileSync(files.upload.path));
    //重定向到结果页
    res.redirect('/public/result.html');
  })
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

** 这个简单的图片上传案例,可以更好的帮助我们了解图片上传的流程,下面我们借助element-ui的upload组件还原真实项目的案例。**

upload上传组件

<template>
	<el-upload
    class="avatar-uploader"
    action="/api/upload"
    :show-file-list="false"
    :on-success="handleUploadSuccess"
    :before-upload="beforeAvatarUpload"
    >
    <img 
    	v-if="imgurl" 
    	:src="imgurl" 
    	class="avatar">
    <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  </el-upload>
</template>
<script>
  export default {
    data() {
      return {
      	imgurl: ''
      }
    },
    methods: {
    	handleAvatarSuccess(res, file) {
        this.imgurl = URL.createObjectURL(file.raw);
      },
      beforeAvatarUpload(file) {
        // 上传前对文件的一些校验处理
      }
    },
    mounted(){
    	
    }
  }
</script>
<style>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409EFF;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

upload API逻辑编写

let path = require('path');

let express = require('express');
let router = express.Router();

let formidable = require('formidable');

router.post('/', function(req, res, next){
  let form = new formidable.IncomingForm();
  form.encoding = 'utf-8'; // 编码
  // 保留扩展名
  form.keepExtensions = true;
  //文件存储路径 最后要注意加 '/' 否则会被存在public下
  form.uploadDir = path.join(__dirname, '../public/images/');
  // 解析 formData 数据
  form.parse(req, (err, fields ,files) => {
    if(err) return next(err)
    let imgPath = files.file.path;
    let imgName = files.file.name;
    console.log(imgName, imgPath);
    // 返回路径和文件名
    res.json({code: 1, data: { name: imgName, path: imgPath }});
  })
});

module.exports = router;

到这里,我们和上面的简单案例对比一下,其实差别并不大,变更的只是最后的数据响应,当然这个案例也只应用于图片上传成功,前端获取到返回的图片地址,然后再和其他的字段一起提交给服务端的情况,如果是上传即对数据库的数据做修改,比如说:头像修改, 就需要在响应数据返回前对数据库进行操作修改。

let path = require('path');

let express = require('express');
let router = express.Router();
// 导入用户信息模型
let User = require('../models/UserModel'); 

let formidable = require('formidable');

router.post('/', function(req, res, next){
  let form = new formidable.IncomingForm();
  form.encoding = 'utf-8'; // 编码
  // 保留扩展名
  form.keepExtensions = true;
  //文件存储路径 最后要注意加 '/' 否则会被存在public下
  form.uploadDir = path.join(__dirname, '../public/images/');
  // 解析 formData 数据
  form.parse(req, (err, fields ,files) => {
    if(err) return next(err);
    //借助username字段进行数据查询
    let username = fields.username; 
    let imgPath = files.file.path;
    let imgName = files.file.name;
    console.log(imgName, imgPath);
    User.updateOne({username: username},{photo: imgPath}, (err, data) => {
    	if(err) return next(err);
    	// 返回处理结果
    	res.json({code: 1, data: data});
    })
    
  })
});

module.exports = router;