Element-ui+Vue实现图片上传组件


talk is cheap show me the code

参考资料

  1. element-ui
  2. vue前端(element-ui),express后端实现上传图片到七牛云

前端组件

  1. 具体的样式配置可以见element-ui的官网
  2. 第一种配置:选择图片后直接上传(action是服务器地址,http-request是上传到服务器前拦截request进行处理的操作):

     <el-upload
         class="upload-demo"
         ref="upload"
         :action=domain
         :http-request=uploadImage
         :file-list="fileList"
         list-type="picture-card"
         :before-upload="beforeUpload"
         multiple>
         <i class="el-icon-plus"></i>
         <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
     </el-upload>
    

    一个参考的uploadImage()函数配置(这里主要需要重命名图片名,我使用mongoose的Types生成一个唯一的ObjectId来进行重命名,也可以使用时间戳等方式):

     async uploadImage(req) {
         const config = {
             headers: {'Content-Type': 'multipart/form-data'}
         };
         let filetype = ''
         if (req.file.type === 'image/png') {
             filetype = 'png'
         } else {
             filetype = 'jpg'
         }
         const keyName = this.bucket +  "-" + Types.ObjectId().toString() +  '.' + fileType;
         const formdata = new FormData();
         formdata.append('file', req.file);
         formdata.append('key', keyName);
         axios.post('/api/uploadImage', formdata, config)
         .then(res => {
             this.fileList.push({
                 name: res.data.name,
                 url: res.data.url,
             });
             console.log('image upload succeed.');
         })
         .catch(err => {
             console.log(err.message)
         })
      },
    

    注意到:我们如果将图片上传到一个跨域服务器是需要进行跨域处理的,后面我们就可以使用’/api’代替服务器地址。
    ① 在Vue中我们可以修改config/index.js:

     proxyTable: {
         '/api': {
             target: 'https://example.com', // 接口的域名
             secure: false,  // 如果是https接口,需要配置这个参数
             changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
             pathRewrite: {'^/api': ''}
         }
     },
    

    ② 在Nuxt中我们可以配置nuxt.config.js:

     modules: ['@nuxtjs/axios','@nuxtjs/proxy'],
     proxy: [
         '/api',
         {
             target: 'https://example.com',
             pathRewrite: {'^/api': '/'}
         }
     ],
    
  3. 另一种配置:点击上传按钮后才上传到服务器(主要将上传组件的:auto-upload设置为false)。如果要实现图片预览可以添加一个处理函数:on-change="handleSuccess"

     handleSuccess(res, files) {
         this.fileList = [];//因为我这里每次上传只选择一张图片,所以清空
         let file = files[files.length - 1];
         let name = file.name;
         if(this.beforeUpload(file.raw)) {
             let imageUrl = window.URL.createObjectURL(file.raw);
             this.imageFile = file;//保存imageFile
             this.fileList.push({name: name, url: imageUrl});
         }
     },
    

    上传到七牛云可以uploadImage()函数进行修改:

     try {
         //获取上传凭证token
         let uptoken = await this.$store.dispatch('getUploadToken');
         const formdata = new FormData();
         formdata.append('file', this.imageFile.raw);
         formdata.append('token', uptoken);//添加凭证
         formdata.append('key', keyName);
         return new Promise((resolve, reject) => {
             axios.post('/upQiniu', formdata, config).then(res => {
             this.imageUrl = 'http://' + this.qiniuaddr + '/' + res.data.key;
             this.fileList = [];
             this.imageFile = null;
             resolve(this.imageUrl);
             });
         });
         } catch (e) {
             console.log('post failed!' + e.message);
         }
    

    后面我们要上传可以直接调用uploadImage函数异步返回图片上传后的路径

后端配置

  1. 上传到七牛云,为了避免暴露密钥,凭证token应该从后端获取:
     const qiniu = require('qiniu')
     //util/onfig/qiniu.js中放置七牛云的访问key和密钥
     import {accessKey, secretKey} from '../util/config/qiniu'
     //例如我在Vuex Store的action中添加了以下函数
     getUploadToken() {
         const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
         const options = {
             scope: 'xxxxx',//你在七牛云的空间名字
         };
         const putPolicy = new qiniu.rs.PutPolicy(options);
         return putPolicy.uploadToken(mac);
     },
    
  2. 上传到本地文件夹,express+multiparty配置图片上传服务器:
     router.post('/uploadImage', async(req, res, next) => {
     var form = new multiparty.Form();
     form.uploadDir = config.imagePath;
     form.maxFieldsSize = 2 * 1024 * 1024;
     var newName;
     await form.parse(req, (err, fields, files) => {
         if(err) {
             return res.status(500).json({message: "upload image failed: " + err});
         }
         newName = fields.key[0];//因为element-ui每次上传一张图片,所以这里考虑取出第一个即可
         let newPath = config.imagePath + newName;
         let originalPath = files.file[0].path;
         try {
             fs.existsSync(originalPath);//本地图片路径是否存在
             fs.renameSync(originalPath,newPath);//重命名,这里最好使用renameSync强制命名
             return res.status(200).json({name: newName, url: config.imageURL + newName});
         } catch (error) {
             return res.status(500).json({message: "upload image failed: " + error});
         }
       });
     });
    

    一些难点

    一个是上传的formdata配置,注意配置config,如果上传失败检查request中的formdata是否为空;另一个是跨域配置。

一个小记录,欢迎指教


文章作者: Susie Chang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Susie Chang !
 上一篇
CSS基础 CSS基础
参考资料 w3school css 盒子模型理解 基础 CSS 指层叠样式表(Cascading Style Sheets),把样式添加到HTML 4.0 中是为了解决内容与表现分离的问题 CSS层叠次序,样式优先级: 内联样式(HTML
2018-08-11
下一篇 
js事件执行机制 js事件执行机制
参考资料 事件循环 Philip Roberts Help I’m stuck in an event loop 这一次,彻底弄懂 JavaScript 执行机制 node基础面试事件环?微任务、宏任务 运行时概念 栈:函数调用形成
2018-08-05
  目录