element-ui+Vue實現的圖片上傳

Ephemera發表於2018-12-12

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是否為空;另一個是跨域配置。


一個小記錄,歡迎指教

相關文章