在线教育-谷粒学院学习笔记(四)

  • Post author:
  • Post category:其他




1 内容介绍

  • 添加讲师实现头像上传功能

    • 阿里云oss存储服务
  • 添加课程分类功能

    • 使用EasyExcel读取excel内容,添加数据
  • 课程分类的列表功能

    • 树形结构显示
  • Nginx使用



2 阿里云存储OSS

  • 登录阿里云官网,开通对象存储oss(在自己的账户中充几毛钱就行)

  • 管理控制台

    在这里插入图片描述



3 Java代码操作阿里云oss

  • 准备:创建操作阿里云oss许可证(id和密钥)

    • 创建AccessKey

      • LTAI5tDgYwJnddqgv7eseKUp

        dI3LnkakIe70taSwDTw6wspXQHN0xq
  • 示例代码
public static void main(String[] args) throws Exception {
    String endpoint = "oss-cn-chengdu.aliyuncs.com";
    String accessKeyId = "LTAI5tDgYwJnddqgv7eseKUp";
    String accessKeySecret = "dI3LnkakIe70taSwDTw6wspXQHN0xq";
    String bucketName = "edu-mys";

    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    // 创建存储空间。
    ossClient.createBucket(bucketName);



4 搭建阿里云oss项目环境

  • 在service下创建service_oss子模块,并引入相关依赖

    <dependencies>
        <!-- 阿里云oss依赖 -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
        </dependency>
        <!-- 日期工具栏依赖 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
    </dependencies>
    
  • 创建配置配置文件:application.properties

    # 服务端口号
    server.port=8002
    # 服务名
    spring.application.name=service-oss
    # 环境设置
    spring.profiles.active=dev
    # 阿里云oss
    aliyun.oss.file.endpoint=oss-cn-chengdu.aliyuncs.com
    aliyun.oss.file.keyid=LTAI5tDgYwJnddqgv7eseKUp
    aliyun.oss.file.keysecret=dI3LnkakIe70taSwDTw6wspXQHN0xq
    # bucket可以在控制台创建,也可以java代码创建
    aliyun.oss.file.bucketname=edu-mys
    
  • 创建启动类 com/mys/oss/OssApplication.java

    @SpringBootApplication
    @ComponentScan(basePackages = {"com.mys"})
    public class OssApplication {
        public static void main(String[] args) {
            SpringApplication.run(OssApplication.class, args);
        }
    }
    
  • 启动

    • 报错:Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class

    • 原因:启动时找数据库配置,但当前模块不需要操作数据库,只是上传到oss功能,没有配置数据库

    • 解决

      • 添加数据库配置

      • 在启动类添加属性,默认不加载数据库配置(推荐)

        @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
        



5 上传头像后端实现


1 创建常量类,读取配置文件内容

// 当项目一启动,spring加载之后,执行spring接口的一个方法
@Component
public class ConstantPropertiesUtils implements InitializingBean {
    // 读取配置文件内容
    @Value("${aliyun.oss.file.endpoint}")
    private String endpoint;
    @Value("${aliyun.oss.file.keyid}")
    private String keyId;
    @Value("${aliyun.oss.file.keysecret}")
    private String keySecret;
    @Value("${aliyun.oss.file.bucketname}")
    private String bucketName;

    // 定义公开的静态常量
    public static String END_POINT;
    public static String ACCESS_KEY_ID;
    public static String ACCESS_KEY_SECRET;
    public static String BUCKET_NAME;

    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT = endpoint;
        ACCESS_KEY_ID = keyId;
        ACCESS_KEY_SECRET = keySecret;
        BUCKET_NAME = bucketName;
    }
}


2 创建controller

@RestController
@RequestMapping("/eduoss/fileoss")
@CrossOrigin
public class OssController {
    @Autowired
    private OssService ossService;
    // 上传头像方法
    @PostMapping
    public R uploadOssFile(MultipartFile file) {
        // 返回上传文件到oss的路径
        String url = ossService.uploadFileAvatar(file);
        return R.ok().data("url", url);
    }
}


3 service实现上传文件到oss过程

@Override
public String uploadFileAvatar(MultipartFile file) {
    // 通过工具类获取值
    String endpoint = ConstantPropertiesUtils.END_POINT;
    String accessKeyId = ConstantPropertiesUtils.ACCESS_KEY_ID;
    String accessKeySecret = ConstantPropertiesUtils.ACCESS_KEY_SECRET;
    String bucketName = ConstantPropertiesUtils.BUCKET_NAME;

    try {
        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        // 获取上传文件的输入流
        InputStream inputStream = file.getInputStream();
        // 获取文件名称
        String fileName = file.getOriginalFilename();
        // 调用oss方法实现上传
        // 参数:bucketName;上传到oss文件路径和文件名称;上传文件输入流
        ossClient.putObject(bucketName, fileName, inputStream);
        // 关闭OSSClient
        ossClient.shutdown();

        // 把上传之后文件路径返回,需手动拼接上传到阿里云的oss路径
        // https://edu-mys.oss-cn-chengdu.aliyuncs.com/yy.JPG
        String url = "https://" + bucketName + "." + endpoint + "/" + fileName;
        return url;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}


4 问题

  • 多次上传相同名称文件,造成最后一次上传把之前的文件覆盖

    • 解决:在文件名称添加随机唯一值,让每个文件名称不同

      // 在文件名称里添加随机唯一的值
      String uuid = UUID.randomUUID().toString().replaceAll("-", "");
      fileName = uuid + fileName;
      
  • 把文件进行分类管理

    • 根据用户分类

    • 根据日期分类,实现年月日分类

      // 把文件按照日期进行分类 2019/11/12/01.jpg
      // 获取当前日期
      String datePath = new DateTime().toString("yyyy/MM/dd");
      // 拼接
      fileName = datePath + "/" + fileName;
      



6 Nginx使用

  • 反向代理服务器

  • 功能

    • 请求转发

      在这里插入图片描述

    • 负载均衡:把请求平均分担到不同的服务器

    • 动静分离

  • 特点:多路复用;关闭窗口后nginx不会停止

    • 停止命令:nginx -s stop


配置nginx实现请求转发功能

  • 下载地址:http://nginx.org/en/download.html

  • 在nginx配置文件中进行配置 nginx.conf

    • 修改ngix默认端口 80->81

      server {
              listen       81;
              server_name  localhost;
              .....
      
    • 配置nginx转发规则

      在http中配置:
      server {
              listen       9001;
              server_name  localhost;
      
              location ~ /eduservice/ {
                  proxy_pass http://localhost:8001;
              }
              location ~ /eduoss/ {
                  proxy_pass http://localhost:8002;
              }
          }
      
  • 修改前端请求地址,改为nginx地址

    • conf/dev.env.js

    • BASE_API: '"http://localhost:9001"',
      
  • nginx重启并测试:先停止再启动



7 上传头像前端实现


1 在添加讲师页面,创建上传组件,实现上传

使用element-ui

找源码里的两个组件,复制到前端项目src/components里面 ImageCropper、PanThumb


2 添加讲师页面中写上传组件

      <!-- 讲师头像 -->
      <el-form-item label="讲师头像">
        <!-- 头衔缩略图 -->
        <pan-thumb :image="teacher.avatar"/>
        <!-- 文件上传按钮 -->
        <el-button type="primary" icon="el-icon-upload" @click="imagecropperShow=true">更换头像</el-button>
        <!--
          v-show:是否显示上传组件
          :key:类似于id,如果一个页面多个图片上传控件,可以做区分
          :url:后台上传的url地址
          @close:关闭上传组件
          @crop-upload-success:上传成功后的回调 -->
        <image-cropper
          v-show="imagecropperShow"
          :width="300"
          :height="300"
          :key="imagecropperKey"
          :url="BASE_API+'/eduoss/fileoss'"
          field="file"
          @close="close"
          @crop-upload-success="cropSuccess"/>
      </el-form-item>


3 使用组件

data()定义变量和初始值

imagecropperShow: false, // 上传弹框组件是否显示
imagecropperKey: 0, // 上传组件key值
BASE_API: process.env.BASE_API, // 获取dev.env.js里面的地址
saveBtnDisabled: false // 保存按钮是否禁用


4 引入和声明组件

import ImageCropper from '@/components/ImageCropper'
import PanThumb from '@/components/PanThumb'

components: { ImageCropper, PanThumb },


5 修改上传接口的地址

:url="BASE_API+'/eduoss/fileoss'"


6 编写close方法和上传成功的方法

// 关闭上传弹框的方法
close() {
    this.imagecropperShow = false
    // 上传组件初始化
    this.imagecropperKey += 1
},
// 上传成功的方法
cropSuccess(data) {
    this.imagecropperShow = false
    this.imagecropperKey += 1
    // 上传之后接口返回图片地址
    this.teacher.avatar = data.url
},



8 课程分类管理介绍

  • 存储结构

在这里插入图片描述

  • 功能

    • 添加——表格
    • 列表



9 EasyExcel工具

  • 特点:一行行读取

  • 写操作


    1 引入依赖

    service_edu/pom.xml

    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>
    

    还需要poi的依赖,注意版本对应


    2 创建实体类

    service_edu/test

    excel/DemoData.java

    @Data
    public class DemoData {
        // 设置表头名称
        @ExcelProperty("学生编号")
        private Integer sno;
        @ExcelProperty("学生姓名")
        private String name;
    }
    


    3 写操作

    public static void main(String[] args) {
        // 1 设置写入文件夹地址和excel名称
        String filename = "D:\\write.xlsx";
    
        // 2 调用easyexcel里面的方法实现写操作
        EasyExcel.write(filename, DemoData.class).sheet("学生列表").doWrite(getData());
    }
    
  • 读操作


    1 创建和excel对应实体类,标记对应列关系

    @Data
    public class DemoData {
        // 设置表头名称
        @ExcelProperty(value = "学生编号", index = 0)
        private Integer sno;
        @ExcelProperty(value = "学生姓名", index = 1)
        private String name;
    }
    


    2 创建监听进行excel文件读取

    public class ExcelListener extends AnalysisEventListener<DemoData> {
        // 一行一行读取excel内容
        @Override
        public void invoke(DemoData demoData, AnalysisContext analysisContext) {
            System.out.println("***" + demoData);
        }
    
        // 读取表头内容
        @Override
        public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
            System.out.println("表头:" + headMap);
        }
    
        // 读取完成之后
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
        }
    }
    


    3 最终方法调用

    String filename = "D:\\write.xlsx";
    EasyExcel.read(filename, DemoData.class,  new ExcelListener()).sheet().doRead();
    



10 课程分类添加功能


1 引入EasyExcel依赖


2 使用代码生成器,生成课程分类的代码

CodeGenerator.java


3 创建实体类和excel对应关系

entity/excel/SubjectData.java

@Data
public class SubjectData {
    @ExcelProperty(index = 0)
    private String oneSubjectName;

    @ExcelProperty(index = 1)
    private String twoSubjectName;
}

**4 controller service **

@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
    @Autowired
    private EduSubjectService subjectService;
    // 添加课程分类,需要获取上传过来的文件,把文件内容读取出来
    @PostMapping("addSubject")
    public R addSubject(MultipartFile file) {
        // 上传过来的Excel文件
        subjectService.saveSubject(file, subjectService);
        return R.ok();
    }
}
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
    // 添加课程分类
    @Override
    public void saveSubject(MultipartFile file, EduSubjectService subjectService) {
        try {
            // 文件输入流
            InputStream in = file.getInputStream();
            // 调用方法进行读取
            EasyExcel.read(in, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
        } catch (Exception e) {
        }
    }
}


5 编写监听器SubjectExcelListener,处理excel文件

public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
    // 因为SubjectExcelListener不能交给spring管理,需要自己new,
    // 不能注入其他对象,不能实现操作数据库,需要自己写
    public EduSubjectService subjectService;
    public SubjectExcelListener() {}
    public SubjectExcelListener(EduSubjectService subjectService) {
        this.subjectService = subjectService;
    }
    // 读取excel内容,一行一行读取
    @Override
    public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
        if (subjectData == null) {
            throw new GuliException(20001, "文件数据为空");
        }
        // 一行行读取,每次读取两个值,第一个是一级分类,第二个是二级分类
        // 添加一级分类
        EduSubject existOneSubject = this.existOneSubject(subjectData.getOneSubjectName());
        if (existOneSubject == null) { // 没有相同的一级分类,进行添加
            existOneSubject = new EduSubject();
            existOneSubject.setParentId("0");
            existOneSubject.setTitle(subjectData.getOneSubjectName());
            subjectService.save(existOneSubject);
        }
        // 获取一级分类id
        String pid = existOneSubject.getId();
        // 添加二级分类
        EduSubject existTwoSubject = this.existTwoSubject(subjectData.getTwoSubjectName(), pid);
        if (existTwoSubject == null) { // 没有相同的一级分类,进行添加
            existTwoSubject = new EduSubject();
            existTwoSubject.setParentId("0");
            existTwoSubject.setTitle(subjectData.getTwoSubjectName());
            subjectService.save(existTwoSubject);
        }
    }
    // 判断一级分类不能重复添加
    private EduSubject existOneSubject(String name) {
        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
        wrapper.eq("title", name);
        wrapper.eq("parent_id", 0);
        EduSubject oneSubject = subjectService.getOne(wrapper);
        return oneSubject;
    }
    // 判断二级分类不能重复添加
    private EduSubject existTwoSubject(String name, String pid) {
        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
        wrapper.eq("title", name);
        wrapper.eq("parent_id", pid);
        EduSubject twoSubject = subjectService.getOne(wrapper);
        return twoSubject;
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    }
}



版权声明:本文为mys_mys原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。