elementUI+nodejs中间件multer+mysql实现图片上传功能

11 篇文章 2 订阅
1 篇文章 0 订阅

vue页面

上传组件

<template>
  <div class="upload">
    <el-upload
      action=""
      :class="{hide:isUpload}"
      :auto-upload="autoUpload"
      :http-request="upload"
      list-type="picture-card"
      multiple
      :limit="limit"
      :file-list="fileList"
      :accept="accept"
      ref="upload"
      :name="name"
      :on-change="handleChange"
      :on-exceed="handleExceed"
    >
      <i slot="default" class="el-icon-plus"></i>
      <div slot="file" slot-scope="{ file }">
        <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
        <span class="el-upload-list__item-actions">
          <span
            class="el-upload-list__item-preview"
            @click="handlePictureCardPreview(file)"
          >
            <i class="el-icon-zoom-in"></i>
          </span>
          <span
            v-if="!disabled"
            class="el-upload-list__item-delete"
            @click="handleRemove(file)"
          >
            <i class="el-icon-delete"></i>
          </span>
        </span>
      </div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="dialogImageUrl" alt="" />
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "Upload",
  props: {
    accept: {
      type: String,
      default: "image/*"
    },
    limit: {
      type: Number,
      default: 5
    },
    name: {
      type: String,
      default: "file"
    },
    autoUpload: {
      type: Boolean,
      default: false
    },
    fileList: {
      type: Array,
      default() {
        return [];
      }
    }
  },
  data() {
    return {
      dialogImageUrl: "",
      dialogVisible: false,
      disabled: false,
      isUpload: false,
    };
  },
  methods: {
    handleChange(file, fileList) {
      // 判断上传文件是否达到限制
      this.isUpload = fileList.length >= this.limit
      // 由于设置自动上传为false,before-upload钩子失效,所以在on-change中检验文件是否符合要求
      const isJPG = ~["image/jpeg", "image/png"].indexOf(file.raw.type);
      const isLt2M = file.raw.size / 1024 / 1024 < 2;
      if (!isJPG) {
        this.$message.error("上传图片只能是 jpg 格式!")
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!")
      } 
      if(!isJPG || !isLt2M){
      // 不符合直接删除该文件
        this.handleRemove(file)
      }
    },
    // 删除图片
    handleRemove(file) {
      let fileList = this.$refs.upload.uploadFiles;
      let index = fileList.findIndex(fileItem => {
        return fileItem.uid === file.uid;
      });
      fileList.splice(index, 1);
      // 由于删除组件有动画功能,所以设置个延迟显示
      setTimeout(()=>{
        this.isUpload = false
      },1000)
    },
    // 预览图片
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.url;
      this.dialogVisible = true;
    },
    // 文件超出限制的提示
    handleExceed(file, fileList) {
      this.$message.error("文件个数超出限制");
    },
    upload() {
      // const formData = new FormData();
      // const file = this.$refs.uploadImg.$refs.upload.uploadFiles;
      // const headerConfig = {
      //   headers: { "Content-Type": "multipart/form-data" }
      // };
      // file.forEach(item => {
      //   formData.append("file", item.raw)
      // });
      // let {data:res} = await this.$http.post("upload/uploadTest", formData, headerConfig);
      // console.log(res,'res');
    }
  }
};
</script>

<style lang="less" scoped>
// 设置上传为none,可以加个动画什么之类的
::v-deep .hide .el-upload--picture-card {
  display: none;
}
</style>

父组件

<template>
  <div class="box">
    <div class="box1">
      <Upload :fileList="fileList" ref="uploadImg" name="avater" />
    </div>

    <el-button class="btn" size="small" type="primary" @click="upload"
      >确定上传</el-button
    >
  </div>
</template>

<script>
import Upload from "@/components/Upload";
export default {
  components: { Upload },
  data() {
    return {
      fileList: [],
    };
  },
  created() {
  },
  methods: {
    async upload() {
    	// 使用的是multer中间件,所以需要传递formdata格式的数据
      const formData = new FormData();
      // 找到需要传递的文件
      const file = this.$refs.uploadImg.$refs.upload.uploadFiles;
      // 设置请求头
      const headerConfig = {
        headers: { "Content-Type": "multipart/form-data" }
      };
      // 遍历 添加文件信息
      // 注意:添加的字段名,需要与后端一样 "file"
      file.forEach(item => {
        formData.append("file", item.raw);
      });
      let { data: res } = await this.$http.post(
        "upload/uploadTest",
        formData,
        headerConfig
      );
    },
  }
};
</script>

<style lang="less" scoped>
.box1 {
  margin: 50px 400px;
}
</style>

Nodejs服务器

const express = require('express');
const router = express.Router();
const multer = require('multer');
const db = require('../model/index')
const fs = require('fs');
const path = require('path');

const upload = multer({
	storage: multer.diskStorage({
		//设置文件存储位置
		destination: function (req, file, cb) {
			let date = new Date();
			let year = date.getFullYear();
			let month = (date.getMonth() + 1).toString().padStart(2, '0');
			let day = date.getDate();
			// 设置存储路径,由于我的静态资源目录是设置的 public,所以设置在 public 文件下
			let dir = `public/uploads/${file.fieldname}/${year}${month}${day}`;
			
			//判断目录是否存在,没有则创建
			if (!fs.existsSync(dir)) {
				fs.mkdirSync(dir, {
					recursive: true
				});
			}
			cb(null, dir);
		},
		//设置文件名称
		filename(req, file, cb) {
			// 重命名文件名,防止重复
			let fileName = file.fieldname + '-' + Date.now() + '-' + file.originalname
			cb(null, fileName);
		}
	})
});

// 常用的两方法:多选用 array(),单选用single()
const multipleFile = upload.array('file', 3)

router.post('/uploadTest', (req, res, next) => {
	multipleFile(req, res, err => {
		if (err instanceof multer.MulterError) {
			console.log('---errMulterError---', err);
		} else if (err) {
			console.log('---err---', err);
		}
		console.log(req.files, 'req.files');
		for (let i = 0; i < req.files.length; i++) {
			let sql = "INSERT INTO test VALUES (NULL,?)"
			// 重新设置存储在数据库的 url 地址,去掉前面的public字符串方便读取
			let destination = req.files[i].destination.substring(6)
			let url = `${destination}/${req.files[i].filename}`
			// console.log(url, 'path');
			
			db(sql, [url], function (err, data) {
				console.log('err', err);
				if (err) {
					res.json({
						code: 500,
						msg: '服务器报错,请稍后重试'
					})
				} else {
					res.json({
						code: 200,
						msg: '服务器响应成功',
						data: data,
					})
				}
			})

		}

	})
})

module.exports = router;

这里有个小注意

在这里插入图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凡小多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值