JavaScript 进阶营JS进步之路Vue

Vue + Element UI + Koa 实现多图片+数据上

2019-06-10  本文已影响1人  Qibing_Fang

一、写作背景

最近在用Vue写一个仿京东、淘宝的电商项目过程中踩了一个大坑 ---- 多图片上传 + 保存

二、问题描述

三、项目介绍及使用的工具

Element UI 中文站点

https://element.eleme.cn/#/zh-CN/component/layout

Element UI Github

https://github.com/ElemeFE/element

四、多图片上传的流程

5、前端代码及解析

<template >
    <div id="goods-add">
        <el-form :model="goodinfo" ref="goodinfo" label-width="100px" class="demo-ruleForm">
            <el-form-item label="名字">
                <el-input v-model="goodinfo.name"></el-input>
            </el-form-item>

            <el-form-item label="价格">
                <el-input v-model="goodinfo.price"></el-input>
            </el-form-item>

            <el-form-item label="描述">
                <el-input v-model="goodinfo.description"></el-input>
            </el-form-item>

            <el-form-item label="品牌">
                <el-input v-model="goodinfo.brand"></el-input>
            </el-form-item>

            <el-form-item label="标签">
                <el-input v-model="goodinfo.label" placeholder="每个标签使用 分开"></el-input>
            </el-form-item>

            <div class="img-upload">
                <el-upload
                    action="#"  // 上传地址,这里我们手动上传,所以不需要填写地址
                    :limit="5"   // 限制上传文件最大数量为5
                    ref="upload"  //标记,我觉得相当于id,可用来选取元素
                    :multiple="true"   // 开启多文件上传
                    :auto-upload="false"   //关闭自动上传
                    :file-list="fileList"  // 上传文件列表
                    list-type="picture-card"> // 上传文件的展示形式,这个是卡片
                    <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
                    <div slot="tip" class="el-upload__tip">上传图片大小不超过500kb</div>
                </el-upload>
            </div>

            <el-form-item>
                <el-button type="primary" @click="submitUpload">立即创建</el-button>
                <el-button @click="resetForm('goodinfo')">重置</el-button>
            </el-form-item>

        </el-form>
    </div>
</template>


<script>
import axios from 'axios'

export default {
    name: 'goods-add',
    methods: {
        submitUpload() {
            // 获取到 上传的所有文件,它是一个数组
            const fileArray = this.$refs.upload.uploadFiles;
            // 实例化FormData对象
            const fd = new FormData();
            // 遍历文件数组,将所有文件存入fd中
            for(let i = 0; i < fileArray.length; i++) {
                // 在这里数组每一项的.raw才是你需要的文件,有疑惑的可以打印到控制台看一下就清楚了
                fd.append('avatar', fileArray[i].raw);
            }
            // 发送HTTP请求,发送数据
            axios({
                url: '/api/view/add-good',
                method: 'post',
                data: fd,
            }).then(res => {
                console.log(res.data);
            })
        }
    }
}
</script>

六、后端Koa使用koa-multer接收文件并保存

6.1 koa-multer的安装与配置

const multer = require('koa-multer');
const storage = multer.diskStorage({
    destination (req, file, cb) {
        // 设置文件的存储目录,需提前创建
        cb(null, '../mall-view/src/assets/img')
    },
    filename (req, file, cb) {
        // 设置 文件名
        const name = file.originalname;
        // 设置文件的后缀名,
        //我这里取的是上传文件的originalname属性的后四位,
        // 即: .png,.jpg等,这样就需要上传文件的后缀名为3位
        const extension = name.substring(name.length - 4);
        cb(null, 'img-' + Date.now() + extension);
    }
})

const upload = multer({ storage: storage })

6.2 使用

router.post('/view/add-good', upload.array('avatar', 5), async (ctx) => {
   const files = ctx.req.files; //上传过来的文件
   ctx.body = {msg: '添加成功'};  //返回数据
})

7、携带form表单中的数据一起上传

针对这个需求,element UI 提供了data属性,用于上传携带的数据,但是我们用不到,因为我们的数据是自己发送http请求自己上传的。

这个问题也困扰了我不少时间,其原因可能是我一开始就想岔了,

7.1 当时我有两个想法:

它们的依据都是这个:
const files = ctx.req.files; //上传过来的文件
const data = ctx.request.body; // 上传的数据
当发送的是文件时, files !== undefined , data === {};
当发送的是数据时, files === undefined , data !== {}

很明显这个方案是行不通的,因为每次发送http请求,此段代码都会运行一次,根本不可能同时获取到所有的数据

当然,方法虽然很笨,但是是能解决问题的,即使这很不可取,但是也不失为一种解决方案

7.2 更加优雅的做法

上面那种方法很明显不好,太浪费资源了,而且还很慢,一旦项目大一点就炸了,所幸我后来在做搜索功能的时候想到了一种更好的办法,这种办法其实我之前在写论坛项目的时候经常用,但是不知道为什么这次没想到,失败啊失败
他就是:通过params发送数据,axios支持这个

所以,改进后的代码如下:
前端:

submitUpload() {
            const session = this.$session.getAll();
            const boss = session.userinfo;
            const goodinfo = this.goodinfo;
            axios({   // 之所以要写这个请求,是因为我需要获取添加商品的商家信息
                method: 'post',
                url: '/api/view/getstore',
                data: { boss_id: boss.boss_id}
            }).then(res => {
                if(res.status === 200) {
                    const store_id = res.data.id;
                    const store_name = res.data.name;
                    const boss_id = boss.boss_id;
                    const boss_name = boss.username;
                    const name = goodinfo.name;
                    const new_price = goodinfo.price;
                    const description = goodinfo.description;
                    const brand = goodinfo.brand;
                    const label = goodinfo.label;
                    const data = {
                        store_id: store_id,
                        store_name: store_name,
                        boss_id: boss_id,
                        boss_name: boss_name,
                        name: name,
                        new_price: new_price,
                        description: description,
                        brand: brand,
                        label: label
                    };
                    const fileArray = this.$refs.upload.uploadFiles;
                    const fd = new FormData();
                    for(let i = 0; i < fileArray.length; i++) {
                        fd.append('avatar', fileArray[i].raw);
                    }
                    axios({
                        url: '/api/view/add-good',
                        method: 'post',
                        data: fd,
                        params: data // 将数据放在就可以上传到服务端
                    }).then(res => {
                        console.log(res.data);

                    })
                }
            })
        },

后端:

router.post('/view/add-good', upload.array('avatar', 5), async (ctx) => {
    const files = ctx.req.files; //上传过来的文件
    // 服务端通过ctx.query 可以获得前端axios中的params里的数据
    const data = ctx.query;  // 上传的数据

    const img_1 = files[0].path;
    const img_2 = files[1].path;
    const img_3 = files[2].path;
    const img_4 = files[3].path;
    const img_5 = files[4].path;
    const store_id = data.store_id;
    const store_name = data.store_name;
    const boss_id = data.boss_id;
    const boss_name = data.boss_name;
    const name = data.name;
    const new_price = data.new_price;
    const description = data.description;
    const brand = data.brand;
    const label = data.label;


    const data1 = [store_id, store_name, boss_id, boss_name, name, new_price, description, brand, img_1, img_2, img_3, img_4, img_5, label];
    await editGood.addGood(data1);

    ctx.body = {msg: '添加成功'};
})

八、结束语

上一篇 下一篇

猜你喜欢

热点阅读