尚硅谷谷粒学院学习笔记5-- 对象存储OSS,整合图片上传,nginx反向代理配置

对象存储OSS

在service模块下创建子模块service_oss

依赖

service-oss上级模块service已经引入service的公共依赖,所以service-oss模块只需引入阿里云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>

配置文件

#服务端口
server.port=8002
#服务名
spring.application.name=service-oss
#环境设置:dev、test、prod
spring.profiles.active=dev
#阿里云 OSS
#不同的服务器,地址不同
aliyun.oss.file.endpoint=oss-cn-beijing.aliyuncs.com
aliyun.oss.file.keyid=your accessKeyId
aliyun.oss.file.keysecret=your accessKeySecret
#bucket可以在控制台创建,也可以使用java代码创建
aliyun.oss.file.bucketname=gulischool-dyk

启动报错

启动时,会自动找数据库的配置,但是当前模块不用操作数据库,只是上传功能,没有配置数据库
在这里插入图片描述

解决启动报错

在@SpringBootApplication注解上加上exclude,解除自动加载DataSourceAutoConfiguration

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ComponentScan(basePackages = {
    
    "com.atguigu"})
public class OssApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OssApplication.class,args);
    }
}

读取配置文件工具类

创建常量读取工具类:ConstantPropertiesUtil.java
使用@Value读取application.properties里的配置内容
用spring的 InitializingBean 的 afterPropertiesSet 来初始化配置信息,这个方法将在所有的属性被初始化后调用。

package com.atguigu.oss.utils;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

@Component
// 常量类,读取配置文件application.properties中的配置
public class AliyunPropertiesUtils 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;


    }
}


文件上传

创建Service接口:OssService

public interface OssService {
    
    
    //文件上传至阿里云
    String uploadFileAvatar(MultipartFile file);
}

OssServiceImpl

package com.atguigu.oss.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.atguigu.oss.service.OssService;
import com.atguigu.oss.utils.AliyunPropertiesUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

@Service
public class OssServiceImpl implements OssService {
    
    
    //上传头像到oss
    @Override
    public String uploadFileAvatar(MultipartFile file) {
    
    
        //工具类获取值
        String endpoint = AliyunPropertiesUtils.END_POINT;
        String accessKeyId = AliyunPropertiesUtils.ACCESS_KEY_ID;
        String accessKeySecret = AliyunPropertiesUtils.ACCESS_KEY_SECRET;
        String backetName = AliyunPropertiesUtils.BUCKET_NAME;

        try {
    
    
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

            // 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
            InputStream inputStream = file.getInputStream();
            //获取文件名
            String filename= file.getOriginalFilename();
            //在文件名称里添加随机唯一的值
            String uuid= UUID.randomUUID().toString().replace("-","");
            filename=uuid+filename;
            
            //把文件按日期分类
            String datePath = new DateTime().toString("yyyy/MM/dd");

            //第一个参数 Backet名称
            //第二个参数 上传到oss文件路径和文件名称
            //第三个参数 上传文件输入流

            //拼接路径
            filename=datePath+"/"+filename;
            ossClient.putObject(backetName, filename, inputStream);
            // 关闭OSSClient。
            ossClient.shutdown();
            //需要把上传文件到阿里云的路径手动拼接出来
           // https://gulischool-dyk.oss-cn-beijing.aliyuncs.com/1.png
            String url="https://"+backetName+"."+endpoint+"/"+filename;

            return url;
        } catch (IOException ioException) {
    
    
            ioException.printStackTrace();
            return null;
        }
    }     
}

controller

@RestController
@RequestMapping("/eduoss/fileoss")
@CrossOrigin
@Api(tags="阿里云文件管理")
public class OssController {
    
    
    @Autowired
    private OssService ossService;
    //上传头像的方法
    @ApiOperation(value = "文件上传")
    @PostMapping("/upload")
    public ResultVo uploadOssFile(@ApiParam(name = "file", value = "文件", required = true)MultipartFile file){
    
    
          //获取上传文件 MultipartFile
        //返回上传路径
       String url= ossService.uploadFileAvatar(file);

        return ResultVo.ok().message("文件上传成功").data("url",url);
    }
}

测试

在这里插入图片描述

配置nginx反向代理

config/dev.env.js,只有一个api地址的配置位置,而我们实际的后端有很多微服务,所以接口地址有很多,
我们可以使用nginx反向代理让不同的api路径分发到不同的api服务器中

nginx快速入门博客链接

在Nginx中配置对应的微服务服务器地址即可
注意应该放在http块里面

  server{
    
    
        listen 9001;
        server_name localhost;

        location ~ /eduservice/{
    
    
            proxy_pass http://localhost:8001;
        }

        location ~ /eduoss/ {
    
    
            proxy_pass http://localhost:8002;
        }
        location ~ /eduvod/ {
    
    
            proxy_pass http://localhost:8003;
        }
        location ~ /cmsservice/ {
    
    
            proxy_pass http://localhost:8004;
        }
        location ~ /edumsm/ {
    
    
            proxy_pass http://localhost:8005;
        }
        location ~ /ucenterservice/ {
    
    
            proxy_pass http://localhost:8006;
        }
        location ~ /orderservice/ {
    
    
            proxy_pass http://localhost:8007;
        }
        location ~ /staservice/ {
    
    
            proxy_pass http://localhost:8001;
        }

    }

重启nginx

进入nginx目录

nginx -s reload

修改前端的BASE_API

config里的dev.env.js

BASE_API: '"http://localhost:9001"'

前端整合图片上传组件

复制头像上传组件

从vue-element-admin复制组件:
vue-element-admin/src/components/ImageCropper
vue-element-admin/src/components/PanThumb

前端添加文件上传组件

 <!-- 讲师头像 -->
      <el-form-item label="讲师头像">
        <!-- 头衔缩略图 -->
        <pan-thumb :image="String(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:上传成功后的回调 
       这里field的值必须和后端接口MultipartFile file的形参名相同
        <input type="file" name="file"/>
      -->
        <image-cropper
          v-show="imagecropperShow"
          :width="300"
          :height="300"
          :key="imagecropperKey"
          :url="BASE_API + '/eduoss/fileoss/upload'"
          field="file"
          @close="close"
          @crop-upload-success="cropSuccess"
        />
      </el-form-item>

引入组件模块,声明初始变量

<script>
import teacherApi from "@/api/edu/teacher";

import ImageCropper from "@/components/ImageCropper";
import PanThumb from "@/components/PanThumb";
export default {
    
    
  components: {
    
     ImageCropper, PanThumb },
  data() {
    
    
    return {
    
    
      teacher: {
    
    
        name: "",
        sort: 0,
        level: 1,
        career: "",
        intro: "",
        avatar: "",
      },
      //上传弹框组件是否显示
      imagecropperShow: false,
      imagecropperKey: 0, //上传组件key值
      BASE_API: process.env.BASE_API, //获取dev.env.js里面地址
      saveBtnDisabled: false, // 保存按钮是否禁用,
    };
  },
 close() {
    
    
      //关闭上传弹框的办法
      this.imagecropperShow = false;
      // 上传失败后,重新打开上传组件时初始化组件,否则显示上一次的上传结果
      this.imagecropperKey = this.imagecropperKey + 1;
    },
    //上传成功方法
    cropSuccess(data) {
    
    
      //这个方法封装好了返回值
      this.imagecropperShow = false;
      //上传之后接口返回图片地址
      this.teacher.avatar = data.url;
      this.imagecropperKey = this.imagecropperKey + 1;
    },

完整版

<template>
  <div class="app-container">
    讲师添加

    <el-form label-width="120px">
      <el-form-item label="讲师名称">
        <el-input v-model="teacher.name" />
      </el-form-item>
      <el-form-item label="讲师排序">
        <el-input-number
          v-model="teacher.sort"
          controls-position="right"
          :min="0"
        />
      </el-form-item>
      <el-form-item label="讲师头衔">
        <el-select v-model="teacher.level" clearable placeholder="请选择">
          <el-option :value="1" label="高级讲师" />
          <el-option :value="2" label="首席讲师" />
        </el-select>
      </el-form-item>
      <el-form-item label="讲师资历">
        <el-input v-model="teacher.career" />
      </el-form-item>
      <el-form-item label="讲师简介">
        <el-input v-model="teacher.intro" :rows="10" type="textarea" />
      </el-form-item>

      <!-- 讲师头像:TODO -->
      <!-- 讲师头像 -->
      <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:上传成功后的回调 
       这里field的值必须和后端接口MultipartFile file的形参名相同
        <input type="file" name="file"/>
      -->
        <image-cropper
          v-show="imagecropperShow"
          :width="300"
          :height="300"
          :key="imagecropperKey"
          :url="BASE_API + '/eduoss/fileoss/upload'"
          field="file"
          @close="close"
          @crop-upload-success="cropSuccess"
        />
      </el-form-item>
      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          @click="saveOrUpdate"
          >保存</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>


<script>
import teacherApi from "@/api/edu/teacher";

import ImageCropper from "@/components/ImageCropper";
import PanThumb from "@/components/PanThumb";
export default {
      
      
  components: {
      
       ImageCropper, PanThumb },
  data() {
      
      
    return {
      
      
      teacher: {
      
      
        name: "",
        sort: 0,
        level: 1,
        career: "",
        intro: "",
        avatar:
          "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
      },
      //上传弹框组件是否显示
      imagecropperShow: false,
      imagecropperKey: 0, //上传组件key值
      BASE_API: process.env.BASE_API, //获取dev.env.js里面地址
      saveBtnDisabled: false, // 保存按钮是否禁用,
    };
  },

  watch: {
      
      
    $route(to, from) {
      
      
      this.init();
    },
  },
  created() {
      
      
    this.init();
  },

  methods: {
      
      
    init() {
      
      
      //判断路径有id值,做修改
      if (this.$route.params && this.$route.params.id) {
      
      
        //从路径获取id值
        const id = this.$route.params.id;

        this.getInfo(id);
      } else {
      
      
        //路径没有id值,做添加
        //清空表单
        this.teacher = {
      
      };
      }
    },

    //根据讲师id查询方法
    getInfo(id) {
      
      
      teacherApi.getTeacherInfoById(id).then((response) => {
      
      
        this.teacher = response.data.teacher;
      });
    },
    saveOrUpdate() {
      
      
      //判断修改还是添加
      //根据teacher是否有id
      if (!this.teacher.id) {
      
      
        //添加
        this.saveTeacher();
      } else {
      
      
        //修改
        this.updateTeacherInfo();
      }
    },
    //添加讲师的方法
    saveTeacher() {
      
      
      teacherApi
        .addTeacher(this.teacher)
        .then((response) => {
      
      
          //提示信息
          this.$message({
      
      
            type: "success",
            message: "添加成功!",
          });

          //回到列表页面 ,路由跳转
          this.$router.push({
      
       path: "/teacher/list" });
        })
        .catch((err) => {
      
      });
    },

    updateTeacherInfo() {
      
      
      teacherApi
        .updateTeacher(this.teacher)
        .then((response) => {
      
      
          //提示信息
          this.$message({
      
      
            type: "success",
            message: "修改成功!",
          });

          //回到列表页面 ,路由跳转
          this.$router.push({
      
       path: "/teacher/list" });
        })
        .catch((err) => {
      
      });
    },

    close() {
      
      
      //关闭上传弹框的办法
      this.imagecropperShow = false;
      // 上传失败后,重新打开上传组件时初始化组件,否则显示上一次的上传结果
      this.imagecropperKey = this.imagecropperKey + 1;
    },
    //上传成功方法
    cropSuccess(data) {
      
      
      //这个方法封装好了返回值
      this.imagecropperShow = false;
      //上传之后接口返回图片地址
      this.teacher.avatar = data.url;
      this.imagecropperKey = this.imagecropperKey + 1;
    },
  },
};
</script>

注意

这一段我卡了很久如果有报错可能是nginx配置错误,或者前端里面路径有错误仔细检查几遍

ps如果觉得前端那个上传图片旋转不好想去掉只需
/src/components/PanThumb/index.vue 代码最后面的三个 hover 注释掉就没有旋转效果了

猜你喜欢

转载自blog.csdn.net/qq_44866153/article/details/120606086
今日推荐