商品服务-品牌管理基础功能

1. 页面展示

  • 直接使用renren-fast逆向生成代码。将vue组件复制到对应位置。

显示效果:

修改权限,先将权限检查功能关闭。(不然不显示增加和删除按钮)

  • src-utils-index.js中修改
/**
 * 是否有权限
 * @param {*} key
 */
export function isAuth (key) {
  // return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
  return true;
}

关闭es6语法检索。(每次启动都报错)

  • build-webpack.base.conf.js中修改。
const createLintingRule = () => ({
  // test: /\.(js|vue)$/,
  // loader: 'eslint-loader',
  // enforce: 'pre',
  // include: [resolve('src'), resolve('test')],
  // options: {
  //   formatter: require('eslint-friendly-formatter'),
  //   emitWarning: !config.dev.showEslintErrorsInOverlay
  // }
})

1.1 效果优化与快速选择开关

element ui与表格相关内容:https://element.eleme.cn/#/zh-CN/component/table

显示状态为Switch 开关形式

  • :active-value="1" 激活(绿色)时是1(和数据库保持一致)。使用:进行双向绑定时,冒号里面是对象。即数字1。而不是字符串1。
  • @change="updateBrandStatus(scope.row)。将该行所有数据传递给函数。
<el-table-column prop="showStatus" header-align="center" align="center" label="显示状态">
  <template slot-scope="scope">
    <!-- 传递该行的所有信息@change="updateBrandStatus(scope.row) -->
    <el-switch v-model="scope.row.showStatus" active-color="#13ce66" inactive-color="#ff4949" :active-value="1" :inactive-value="0" @change="updateBrandStatus(scope.row)">    
    </el-switch>
  </template>
</el-table-column>
  • 方法体获取需要传递给后端的brandIdshowStatus
updateBrandStatus(data) {
  //解构获取需要的两个值
  let {brandId,showStatus}=data;
  //后台发送修改信息。
  this.$http({
  url: this.$http.adornUrl('/product/brand/update'),
  method: 'post',
  data: this.$http.adornData({brandId,showStatus}, false)
  }).then(({ data }) => {
    this.$message({
      type:"success",
      message:"状态更新成功"
    })
   });
},

页面效果

  • 打开时,数据库状态变为1。关闭时,数据库状态变为0。

点击新增时,品牌logo地址应该是文件的网络地址。表格中显示的也应该是图片。

  • 使用阿里云云存储OSS存放上传的图片。并获取回调的图片url展示在表格中。

2. 云存储开通与使用

  • 使用阿里云云存储来存储项目需要的文件,按量收费。

开通OSS对象服务

2.1 SpringCloud Alibaba-OSS 简介

对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

专业术语:

中文 英文 说明
存储空间 Bucket 存储空间是您用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间。
对象/文件 Object 对象是 OSS 存储数据的基本单元,也被称为OSS的文件。对象由元信息(Object Meta)、用户数据(Data)和文件名(Key)组成。对象由存储空间内部唯一的Key来标识。
地域 Region 地域表示 OSS 的数据中心所在物理位置。您可以根据费用、请求来源等综合选择数据存储的地域。详情请查看OSS已经开通的Region。
访问域名 Endpoint Endpoint 表示OSS对外服务的访问域名。OSS以HTTP RESTful API的形式对外提供服务,当访问不同地域的时候,需要不同的域名。通过内网和外网访问同一个地域所需要的域名也是不同的。具体的内容请参见各个Region对应的Endpoint。
访问密钥 AccessKey AccessKey,简称 AK,指的是访问身份验证中用到的AccessKeyId 和AccessKeySecret。OSS通过使用AccessKeyId 和AccessKeySecret对称加密的方法来验证某个请求的发送者身份。AccessKeyId用于标识用户,AccessKeySecret是用户用于加密签名字符串和OSS用来验证签名字符串的密钥,其中AccessKeySecret 必须保密。

本项目采用服务端签名后直传方式上传文件。

  • 阿里云OSS账号和密码保存在服务器。
  • 浏览器上传文件,找服务器要一个签名。(包含账号和密码加密值)。将文件和签名发给阿里云OSS服务。阿里云对签名进行验证。验证通过,同意上传。
  • 这样做的好处:避免账号密码泄露。节省文件上传带宽(文件不用经过服务器。直接由浏览器上传给OSS)。

新建一个bucket(存储空间),选择需要功能。低频访问和公共读。

2.2 使用Java SDK操作OSS(测试)

SDK介绍:https://help.aliyun.com/document_detail/32009.html

1.导入依赖

  • 在common服务中导入。其他服务都需要该依赖。
  • 实际项目需要将该依赖存放在一个单独的用来管理第三方服务。
<!--OSS文件上传依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
  1. 创建阿里云账号子账户
  • 不能直接使用阿里云账号来授权给OSS使用。权限过大,有风险。
  • 新建一个子账号。该账号只能使用OSS服务。且只能通过API调用使用(勾选)。

  1. 对该账号授权
  • 管理对象存储服务(OSS)权限。
  • 包括读和写权限。

  1. 在product服务编写配置文件
  • 测试使用,实际配置文件都应该存在第三方服务中。
spring:
  cloud:
    alicloud:
      access-key: LTAI5tFxxxxxxxxxxxxx
      secret-key: 0J0heLwByxxxxxxxxxxxxxxxxx
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
  1. 编写测试方法
  • 记得添加注解@RunWith(SpringRunner.class)
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallProductApplicationTests {
    @Autowired
    BrandService brandService;
    @Autowired
    OSSClient ossClient;

    @Test
    public void testUpload() throws FileNotFoundException {
        FileInputStream inputStream = new FileInputStream("F:\\文件\\照片\\极简壁纸\\bcfe1a09ed9.jpg");
        ossClient.putObject("cxnph","bcfe1a09ed9.jpg",inputStream);
        ossClient.shutdown();
        System.out.println("上传完成");
    } 
}
  1. 阿里云上传效果展示

2.3 使用Java SDK操作OSS(正式)

之后有许多第三方服务。新建一个springboot工程,统一用来管理这些第三方服务。

2.3.1 引入依赖

  • 三方服务暂时不需要数据配置。引入gulimall-common时排除mybatis-plus依赖。
<dependency>
    <groupId>com.atguigu.gulimall</groupId>
    <artifactId>gulimall-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <exclusions>
       <exclusion>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
       </exclusion>
     </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--OSS文件上传依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.3.2 添加依赖管理配置

介绍:

  • Maven中的dependencyManagement元素提供了一种管理依赖版本号的方式。
  • 在dependencyManagement元素中声明所依赖的jar包的版本号等信息,那么所有子项目再次引入此依赖jar包时则无需显式的列出版本号。
  • Maven会沿着父子层级向上寻找拥有dependencyManagement 元素的项目,然后使用它指定的版本号

优点:

  • 如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号。
  • 当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要逐个修改子项目。
  • 如果某个子项目需要另外的一个版本,只需要声明version即可。

注意事项:

  • dependencyManagement中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖。

由于其他服务基本上都需要引用第三方服务。所以在该服务中添加依赖管理配置。

  • spring-cloud和alibaba-cloud
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.3.3 配置nacos配置中心

nacos新建命名空间,第三方配置放在该命名空间下

bootstrap.properties

spring.application.name=gulimall-third-party
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=f085e776-8540-4715-87c5-7da63183a98a

2.3.4 配置nacos注册中心

  • access-key等应该是在配置中心配置。为了开发方便,先在yml中配置。
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: LTAI5tFxxxxxxxxxxxxx
      secret-key: 0J0heLwByxxxxxxxxxxxxxxxxx
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
  application:
    name: gulimall-third-party

server:
  port: 30000
logging:
  level:
    com.atguigu.gulimall: debug

2.3.5 开启服务发现注解

@EnableDiscoveryClient
@SpringBootApplication
public class GulimallThirdPartyApplication {
    public static void main(String[] args) {
        SpringApplication.run(GulimallThirdPartyApplication.class, args);
    }
}

启动测试,看服务在nacos有没有启动

  • 服务启动成功,在nacos能发现该服务。

2.3.6 在该服务(第三方服务)进行上传测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallThirdPartyApplicationTests {
    @Autowired
    OSSClient ossClient;

    @Test
    public void testUpload() throws FileNotFoundException {
        FileInputStream inputStream = new FileInputStream("F:\\文件\\照片\\极简壁纸\\bcfe1a09ed9.jpg");
        ossClient.putObject("xxxxx","bcfe1a09ed9.jpg",inputStream);
        ossClient.shutdown();
        System.out.println("上传完成");
    }
}

2.4 服务端签名后直传具体实现

2.4.1 ossControlle获取文件上传签名请求

  • 新建一个ossController,用来给浏览器获取文件上传签名。将结果封装在data中。
@RestController
public class OssController {
    @Autowired
    OSS ossClient;
    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;

    @RequestMapping("/oss/policy")
    public R policy(){

        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        // 用户上传文件时指定的前缀。以日期为文件夹。每一天新建一个文件夹,存放图片
        String dir = format+"/";

        // 创建OSSClient实例。
        Map<String, String> respMap=null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // respMap.put("expire", formatISO8601Date(expiration));
        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        } finally {
            ossClient.shutdown();
        }
        return R.ok().put("data",respMap);
    }
}

2.4.2 配置bucket

yml配置(第三方服务)

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: LTAI5tFdYPxxxxxxxxxxxxxx
      secret-key: 0J0heLwByD2Jxxxxxxxxxxxxxx
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: xxxxx

2.4.3 配置网关

  • 编写controller之后,向localhost:30000/oss/policy发送请求即可获取签名数据。
  • 所有请求都需要交给网关处理。所以需要在网关进行路径映射。
- id: third_party_router
          uri: lb://gulimall-third-party
          # 前端请求都是/api开头的。都转给renren-fast服务
          predicates:
            - Path=/api/thirdparty/**
          # 目前请求:http://localhost:88/api/thirdparty/oss/policy
          # 实际需要:http://localhost:30000/oss/policy
          filters:
            - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}

2.4.4 请求测试

给网关http://localhost:88/api/thirdparty/oss/policy发请求测试

  • 成功获取签名数据。

2.5 结合前端实现单文件上传

  • 编写一个单文件上传组件singleUpload.vue。并将其对外暴露。
  • brand-add-or-update.vue组件中引用该组件。

2.5.1 点击新增按钮

  • 点击新增按钮,调用单击函数addOrUpdateHandle()
  • this.addOrUpdateVisible = true打开新增页面。
<el-button v-if="isAuth('product:brand:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>

import AddOrUpdate from "./brand-add-or-update";
export default {
  data() {
    return {
      addOrUpdateVisible: false,
    };
  },
  components: {
    AddOrUpdate,
  },
}

addOrUpdateHandle(id) {
  this.addOrUpdateVisible = true;
  this.$nextTick(() => {
    this.$refs.addOrUpdate.init(id);
  });
},

2.5.2 点击上传按钮

  • 调用SingleUpload组件。
  • 将组件绑定dataForm.logo值。获取文件上传后的url
<el-form-item label="品牌logo地址" prop="logo">
  <single-upload v-model="dataForm.logo"></single-upload>
</el-form-item>

import SingleUpload from "../../../components/upload/singleUpload.vue";
export default {
  components: { SingleUpload: SingleUpload },
}

2.5.3 文件上传实现

<template> 
  <div>
    <!-- 替换自己的oss bucket域名地址 -->
    <el-upload
      action="http://xxxxx.oss-cn-beijing.aliyuncs.com"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showFileList"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="fileList[0].url" alt="">
    </el-dialog>
  </div>
</template>

文件上传前获取签名

  • 获取前面,将后端获取的data中的值保存在_self.dataObj中。
beforeUpload(file) {
  let _self = this;
  return new Promise((resolve, reject) => {
    policy().then(response => {
      console.log("响应数据",response);
      _self.dataObj.policy = response.data.policy;
      _self.dataObj.signature = response.data.signature;
      _self.dataObj.ossaccessKeyId = response.data.accessid;
      _self.dataObj.key = response.data.dir +getUUID()+'_${filename}';
      _self.dataObj.dir = response.data.dir;
      _self.dataObj.host = response.data.host;
      resolve(true)
    }).catch(err => {
      reject(false)
    })
  })
},

向后端发请求获取签名值

policy.js

import http from '@/utils/httpRequest.js'
export function policy() {
   return  new Promise((resolve,reject)=>{
        http({
            url: http.adornUrl("/thirdparty/oss/policy"),
            method: "get",
            params: http.adornParams({})
        }).then(({ data }) => {
            resolve(data);
        })
    });
}

前面获取后的操作。

  • 获取拼接url路径地址。(即该图片上传的网络路径url)
  • 父组件可以使用 props 把数据传给子组件。
  • 子组件可以使用 $emit,让父组件监听到自定义事件。(给父组件传递url值。)
emitInput(val) {
  this.$emit('input', val)
},

handleUploadSuccess(res, file) {
  console.log("上传成功...")
  this.showFileList = true;
  this.fileList.pop();
  //关键代码。
  this.fileList.push({name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
  this.emitInput(this.fileList[0].url);
}

2.5.4 文件上传测试

上传一张图片进行测试。发现跨域问题。

2.6 解决跨域问题

在阿里云OSS上配置跨域问题。(正式环境要配置来源,不能配置*)

再次上传设置,图片成功上传并回显到该页面

posted @ 2021-12-31 17:58  初夏那片海  阅读(135)  评论(0)    收藏  举报