关于vue.js:vueeloss上传预览下载删除存数据库前端操作oss的详细实现

5次阅读

共计 8754 个字符,预计需要花费 22 分钟才能阅读完成。

注:
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);
}

// 上传 oss
export 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()};

// 删除 oss
export 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>
正文完
 0