注:
1. 个别状况不会让前端间接操作oss(增删查),可是我遇到了这样的需要
2. 默认曾经开明oss,在oss官网实现了配置
3. 须要应用到element-ui的upload组件

点击跳转oss的官网API
点击跳转element的官网API

需要形容

  1. 前端间接操作oss,类型能够是任何类型。本demo以图片和压缩包为例,图片能够预览、删除;压缩包能够下载、删除。
  2. 在表单填写时,一个表单可能有多个不同类型文件的须要上传;表单波及新增/查看/编辑性能,复用性要强。
  3. 新增表单的具体需要:表单新增之后oss即可编辑,一旦新增过文件后要么删除已上传文件,要么提交表单,提交不胜利依据后端提醒从新批改表单的谬误从新提交,或者删除放弃提交。
  4. 查看表单的具体需要:查看表单不可操作oss,然而对于图片能够进行预览,压缩包能够进行下载。
  5. 批改表单的具体需要:一旦批改过文件(删除,新增,批改)必须提交表单,提交不胜利依据后端提醒从新批改表单的谬误从新提交,直到胜利为止。
  6. F5强刷也要保障上述5条,私密麻生,我太难了,我没做

成果展现

新增

查看

编辑

图片预览(放大)

新增但不提交的提醒

删除但不提交的提醒

批改但不提交的提醒

后期筹备

  1. npm install ali-oss
  2. 须要运维提供accessKeyIdaccessKeySecretbucket
  3. 其中bucket最好辨别开发环境和生产环境
  4. oss官网举荐应用STS长期受权拜访,我这里没有用,有趣味的话能够移步OSS长期受权

utils.js(工具类)

import axios from 'axios'import OSS from 'ali-oss'import UUID4 from 'uuid/v4'// 下载oss指定地址export function downloadURL(url) {  let link = document.createElement('a');  link.style.display = 'none';  link.href = url;  document.body.appendChild(link);  link.click();  document.body.removeChild(link);}// 上传ossexport function ossUpload(picForProject, fileBuffer) {  let bucketName = '';  // 判断环境 生产环境并且不为测试状态  process.env.NODE_ENV === 'production' ? bucketName = '填你本人申请的' : bucketName = '填你本人申请的';  let client = new OSS({    region: 'oss-cn-beijing',    secure: true, // https    accessKeyId: '填你本人申请的',    accessKeySecret: '填你本人申请的',    bucket: bucketName  });  const fileName = UUID4().toUpperCase().replace(/-/g, '')  const suffix = fileBuffer.name.substr(fileBuffer.name.indexOf('.'))  const url = `${picForProject}${fileName}${suffix}`  async function put() {    try {      await client.put(url, fileBuffer)      // await xxx(url) 在这里也能够间接把地址传给后端      return { 'code': 200, 'url': url }    } catch (e) {      return e.message    }  }  return put()};// 获取oss地址export function showPrivateOss(picName) {  // 这里和上传oss的client是一样的,应该放在store外面同一治理的,demo这里写在这里了  let bucketName = '';  process.env.NODE_ENV === 'production' ? bucketName = '填你本人申请的' : bucketName = '填你本人申请的';  let client = new OSS({    secure: true, // https    accessKeyId: '填你本人申请的',    accessKeySecret: '填你本人申请的',    bucket: bucketName,    endpoint: 'oss-cn-beijing.aliyuncs.com'  });  async function getOssAddr() {    try {      let signUrl = client.signatureUrl(picName, { expires: 1800 }); // expires单位为秒      return { 'code': 200, 'url': signUrl }    } catch (e) {      return e.message    }  }  return getOssAddr()};// 删除ossexport function deletePrivateOss(picName) {  let bucketName = '';  process.env.NODE_ENV === 'production' ? bucketName = '填你本人申请的' : bucketName = '填你本人申请的';  let client = new OSS({    secure: true, // https    accessKeyId: '填你本人申请的',    accessKeySecret: '填你本人申请的',    bucket: bucketName,    endpoint: 'oss-cn-beijing.aliyuncs.com'  });  async function deleteOssAddr() {    try {      return client.delete(picName);    } catch (e) {      return e.message    }  }  return deleteOssAddr()};

oss.vue(oss组件)

<template>  <div>    <el-upload      class="avatar-uploader"      action="oss地址"      :show-file-list="false"      :http-request="uploadToOss"      :before-upload="beforeAvatarUpload"      :disabled="disabled"      :accept="accept"      :typeName="typeName"    >      <div v-loading="loading">        <template v-if="haveUrl">          <img :src="imgUrl" class="avatar" />          <span class="show_icon" v-if="!disabled">            <i class="el-icon-view" @click.stop="handleView()" v-if="typeName==='图片'"></i>            <i class="el-icon-download" @click.stop="handleDownload()" v-else></i>            <i class="el-icon-delete" @click.stop="handleRemove()"></i>          </span>          <span class="show_icon" v-else>            <i class="el-icon-view" @click.stop="handleView()" v-if="typeName==='图片'"></i>            <i class="el-icon-download" @click.stop="handleDownload()" v-else></i>          </span>        </template>        <i v-else class="el-icon-plus avatar-uploader-icon"></i>      </div>    </el-upload>    <!--查看图片-->    <el-dialog title="照片详情" :visible.sync="showBigPic" width="720px" append-to-body>      <img :src="imgUrl" width="100%" />    </el-dialog>  </div></template><script>// 援用工具类(下载指定地址的oss文件、上传oss、展现oss图片、删除指定地址的oss文件)import {  downloadURL,  ossUpload,  showPrivateOss,  deletePrivateOss,} from "@/assets/scripts/utils";// 二次封装的element的message组件,代码略import { errorMsg, successMsg } from "@/components/message";export default {  props: {    //对文件的限度    limit: {      type: Object,      default: function () {        return {          size: 5,          types: ["image/jpeg", "image/png"],        };      },    },    // 是否有url    haveUrl: {      type: Boolean,      default: false,    },    // 是否禁用    disabled: {      type: Boolean,      default: false,    },    // url的名称    urlName: {      type: String,      default: "",    },    // 对应的后端字段名,同一页面可能有多个须要上传oss的    name: {      type: String,      default: "",    },    // 上传弹窗,过滤文件类型    accept: {      type: String,      default: "image/*",    },    // 上传文件类型(文件大小过大提醒语也会用到)    typeName: {      type: String,      default: "图片",    },  },  data() {    return {      showBigPic: false, // 预览图片      imgUrl: null,     // 图片地址      ossFileName: "", // oss文件名      loading: false, // 是否上传中    };  },  watch: {    urlName: {      handler(newValue, oldValue) {        if (newValue) {          if (this.typeName !== "图片" && this.haveUrl) {            this.imgUrl = require("@/assets/images/uploaded.jpg");          } else {            showPrivateOss(newValue).then((res) => {              if (res && res.code) {                this.imgUrl = res.url;              } else {                errorMsg(res);              }            });          }        }      },      immediate: true,    },  },  methods: {    handleView() {      this.showBigPic = true;    },    handleDownload() {      showPrivateOss(this.urlName).then((res) => {        if (res && res.code) {          downloadURL(res.url);        } else {          errorMsg(res);        }      });    },    handleRemove() {      let _this = this;      deletePrivateOss(this.urlName).then((res) => {        if (res.res.status === 204) {          successMsg();          // ossDone参数:url文件地址, haveUrl是否有地址, name字段名          this.$emit("callback", null, false, _this.name);        } else {          errorMsg(res);        }      });    },    // 上传oss    uploadToOss(elUpload) {      this.loading = true;      let _this = this;      ossUpload("back-image/" + this.ossFileName, elUpload.file).then((res) => {        if (res && res.code) {          // ossDone参数:url文件地址, haveUrl是否有地址, name字段名          this.$emit("callback", res.url, true, _this.name);        } else {          errorMsg(res);        }        this.loading = false;      });    },    // 图片上传前 验证图片大小    beforeAvatarUpload(file) {      let isSize = false;      if (this.limit.sizeType === "kb") {        isSize = file.size / 1024 < this.limit.size;      } else {        isSize = file.size / 1024 / 1024 < this.limit.size;      }      if (!isSize) {        errorMsg(          `${this.typeName}大小不能超过${this.limit.size}${            this.limit.sizeType ? this.limit.sizeType : "MB"          }!`        );      }      return isSize;    },  },};</script><style lang="less" scoped>.show_icon {  display: inline-block;  position: absolute;  top: 0;  right: 0;  width: 108px;  height: 72px;  i {    display: none;  }  &:hover {    background-color: rgba(0, 0, 0, 0.5);    i {      display: inline;      line-height: 72px;      color: #fff;      font-size: 20px;      &:not(:last-child) {        margin-right: 10px;      }    }  }}</style>

addOrEdit.vue(表单组件;新增/查看/编辑为同一个组件)

/***这是一个弹窗组件,为了代码简洁,删除了其余表单内容**父组件传来id,即断定是查看/编辑,须要申请后端详情**:before-close须要带参数,重要!!*/<template>  <el-dialog    @open="setData"    :visible.sync="params.show"    :before-close="() => cancel(false)"    :close-on-click-modal="false"    width="1100px"    append-to-body  >    <div slot="title">      {{params.title}}      <span class="title_required">        <span>*</span>为必填项      </span>    </div>    <el-form      :model="addForm"      :inline="true"      ref="addForm"      label-position="left"      label-width="125px"    >      // 其余item略      <el-form-item        label="营业执照照片"        prop="businessLicenseUrl"        :rules="[{required: true,message: '不能为空'}]"      >        <oss          @callback="ossDone"          :haveUrl="haveUrl_businessLicenseUrl"          :disabled="isDisabled"          :urlName="addForm.businessLicenseUrl"          name="businessLicenseUrl"        />      </el-form-item>      <el-form-item label="附件">        <oss          @callback="ossDone"          :limit="limit"          :haveUrl="haveUrl_attachment"          :urlName="addForm.attachment"          :disabled="isDisabled"          accept=".zip, .rar"          name="attachment"          typeName="压缩包"        />      </el-form-item>    </el-form>    <span slot="footer">      <el-button @click="cancel(false)" class="cancelBtn">取 消</el-button>      <el-button        @click="submit"        type="primary"        class="confirmBtn"        v-if="this.params.title !=='查看'"      >确 定</el-button>    </span>  </el-dialog></template><script>import oss from "@/components/oss";import { methodPost } from "@/api";  // 新增/批改用【代码略】import { GET_DETAILS } from "@/api/company"; //详情【代码略】import { errorMsg, successMsg } from "@/components/message"; // 二次封装的message【代码略】export default {  components: { oss },  data() {    return {      // 是否有执照      haveUrl_businessLicenseUrl: false,      // 是否有附件      haveUrl_attachment: false,      // 编辑时,附件的返回值(作为flag)      flag_attachment: null,      // 编辑时,执照的返回值(作为flag)      flag_businessLicenseUrl: null,      // 附件的大小      limit: {        size: 10      }    };  },  methods: {    // 操作oss后的$emit    ossDone(url, haveUrl, name) {      this.$set(this.addForm, `${name}`, url);      this.$set(this, `haveUrl_${name}`, haveUrl);    },    // 弹窗初始化    setData() {      this.addForm = {};      this.isDisabled = false;      if (this.params.title !== "新增") {        this.haveUrl_businessLicenseUrl = true;        this.getDetails({ id: this.params.id });      } else {        this.haveUrl_attachment = false;        this.haveUrl_businessLicenseUrl = false;        this.flag_attachment = null;        this.flag_businessLicenseUrl = null;      }    },    getDetails(id) {      GET_DETAILS(id).then((res) => {        if (res.code === 10000) {          this.addForm = res.result;          this.flag_attachment = res.result.attachment;          this.flag_businessLicenseUrl = res.result.businessLicenseUrl;          if (res.result.attachment) {            this.haveUrl_attachment = true;          } else {            this.haveUrl_attachment = false;          }        }        if (this.params.title === "查看") {          this.isDisabled = true;        }      });    },    submit() {      // 新增/编辑      this.$refs.addForm.validate((valid) => {        if (valid) {          let url = "/company/";          if (this.params.data) {            url += "update";          } else {            url += "insert";          }          methodPost(url, this.addForm, true).then((res) => {            if (res.code === 10000) {              successMsg();              this.cancel(true);            }          });        } else {          errorMsg();        }      });    },    cancel(refresh) {      if (this.params.title !== "新增") {        if (!refresh) {          if (this.flag_attachment !== this.addForm.attachment) {            errorMsg("批改附件后,请点击‘确定’按钮");            return;          }          if (            this.flag_businessLicenseUrl !== this.addForm.businessLicenseUrl          ) {            errorMsg("批改营业执照后,请点击‘确定’按钮");            return;          }        }      } else {        if (this.addForm.businessLicenseUrl || this.addForm.attachment) {          errorMsg("点击‘确定’提交新增内容,或删除已上传文件");          return;        }      }      this.$refs.addForm.resetFields();      this.$emit("callback", refresh); // 告诉父组件刷新list页,按理论取舍,父组件代码略    },  },};</script>