实现讲师头像上传功能
一、阿里云OSS存储服务
- 打开阿里云网站:https://www.aliyun.com/
- 注册阿里云账号
- 使用注册好的账号登录
- 找到阿里云OSS
- 开通
- 管理控制台的使用
- 使用OSS,首先需要创建buckets
- 点击文件管理可以上传文件
二、Java代码操作阿里云OSS,上传文件到阿里云OSS操作
准备工作:创建操作阿里云OSS的许可证(阿里云颁发的id和密钥)
新建service_oss子模块,在pom.xml中添加依赖
<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 # 环境设置:dev、test、prod spring.profiles.active=dev # 阿里云 OSS # 不同的服务器,地址不同 aliyun.oss.file.endpoint=oss-cn-beijing.aliyuncs.com aliyun.oss.file.keyid=LTAI5tCw6f4FczR3g6NV5Wan aliyun.oss.file.keysecret=9m0CaAa2eITtKqkGhv1o1J4AqzINbU # bucket可以在控制台创建,也可以使用java代码创建 aliyun.oss.file.bucketname=prannt-edu-1010
项目启动时报错:启动的时候去找数据库的配置了,但这个模块不需要操作数据库,只是做上传到oss功能
解决方案:在启动类上添加属性,默认不加载数据库的配置:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
三、上传讲师头像的代码
创建常量类,读取配置文件的相关内容
// 创建常量类,读取配置文件的相关内容 // 当项目一启动,spring中有一个接口,spring加载之后执行接口的一个方法 @Component public class ConstantPropertiesUtils implements InitializingBean { // 读取配置文件的内容 // @Value()注解的作用:将括号中的值注入到endPoint中 @Value("${aliyun.oss.file.endpoint}") private String endPoint; // 地域节点 @Value("{aliyun.oss.file.keyid}") private String keyId; // 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对外可以使用,使用方法:类名.END_POINT END_POINT = endPoint; ACCESS_KEY_ID = keyId; ACCESS_KEY_SECRET = keySecret; BUCKET_NAME = bucketName; } }
创建controller
@RestController @RequestMapping("eduoss/fileoss") public class OssController { @Autowired private OssService ossService; /** * 上传头像的方法 * @param file 上传的文件 * @return 上传到 oss的路径(网址) */ @PostMapping public R uploadOssFile(MultipartFile file){ String url = ossService.uploadFileAvatar(file); return R.ok().data("url",url); } }
在service层实现上传文件到oss的过程
@Service public class OssServiceImpl implements OssService { @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; OSS ossClient = null; try { // 创建OSSClient实例。 ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 获取上传文件的输入流 InputStream inputStream = file.getInputStream(); // 获取文件名称 String filename = file.getOriginalFilename(); // 调用oss方法实现上传 // 第一个参数:bucket名称 // 第二个参数:上传到oss文件路径和文件名称 // 第三个参数:上传文件的输入流 ossClient.putObject(bucketName, filename, inputStream); // 关闭OSSClient。 ossClient.shutdown(); // 把上传之后文件的路径返回 // 需要把上传到阿里云oss的路径手动拼接并返回 String url = "https://" + bucketName + "." + endpoint + "/" + filename; return url; } catch (IOException e) { e.printStackTrace(); return null; } } }
在swagger中进行测试
http://localhost:8002/swagger-ui.html
讲师头像上传功能完善
多次上传相同名称的文件,造成最后一次上传把之前上传的文件覆盖
// 在文件名称中添加随机唯一值,让每个文件名称不同 // 1.在文件名称里添加随机唯一的值,让每个文件名称都不相同 String uuid = UUID.randomUUID().toString().replaceAll("-",""); filename = uuid + filename;
把文件分类管理(根据日期分类,实现年月日分类)
// 2.把文件按照日期进行分类 // 2019/11/12/01.jpg // 获取当前日期 String datePath = new DateTime().toString("yyyy/MM/dd"); // 拼接 filename = datePath + "/" + filename;
四、Nginx的使用
Nginx:反向代理服务器,可以用来做请求转发、负载均衡、动静分离
什么是请求转发?
什么是负载均衡?
什么是动静分离?
把Java代码和页面分开进行部署
使用cmd启动nginx
nginx.exe
如果关闭cmd窗口,nginx不会停止,可以使用命令停止
nginx.exe -s stop
配置nginx实现请求转发的功能
找到nginx配置文件
在nginx.conf里进行配置
前端(config/dev.env.js)中的端口号改成
BASE_API: '"http://localhost:9001"',
重新启动nginx
nginx.exe -s stop nginx.exe
测试
// 启动后端的两个端口(8001和8002) // 重启前端代码 npm run dev
五、上传讲师头像前端实现
在讲师添加页面,创建上传组件,实现上传功能(使用element-ui中的组件)
在添加讲师页面使用这两个组件(以下几步都在save.vue中)
使用组件
引入组件和声明组件
添加课程分类的功能(使用EasyExcel读取excel内容添加数据)
一、创建数据库表
具体的sql
语句见:**谷粒学院sql**
二、EasyTask操作
在service_edu的test下进行测试
引入EasyTask的依赖
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.0.5</version> </dependency> </dependencies> <!--还需要的依赖,已经在父工程中引入过了-->
创建实体类(需要和excel中的字段对应)
@Data public class DemoData { // 设置excel表头的名称(一级分类和二级分类) @ExcelProperty("学生编号") private Integer sno; // 学生编号 @ExcelProperty("学生姓名") private String sname; // 学生姓名 }
excel写操作
public class TestEasyExcel { public static void main(String[] args) { // 实现excel写操作 // 1.设置写入文件夹的地址和excel文件名称 String filename = "C:\\write.xls"; // 2.调用EasyExcel里面的方法实现写操作 // 参数1:文件的路径名称;参数2:实体类class EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData()); } // 创建一个方法,返回list集合 private static List<DemoData> getData(){ List<DemoData> list = new ArrayList<>(); DemoData data = new DemoData(); for (int i = 0; i < 10; i++) { data.setSno(i); data.setSname("lucy" + i); list.add(data); } return list; } }
excel读操作
// 1.创建实体类 @Data public class DemoData2 { // 设置excel表头的名称(一级分类和二级分类) @ExcelProperty(value = "学生编号",index = 0) private String sno; // 学生编号 @ExcelProperty(value = "学生姓名",index = 1) private String sname; // 学生姓名 } // 2.创建监听进行excel文件读取 public class ExcelListener extends AnalysisEventListener<DemoData2> { // 一行一行的读取excel内容 @Override public void invoke(DemoData2 demoData2, AnalysisContext analysisContext) { System.out.println("****" + demoData2); } // 读取表头的内容 // 该方法可以在AnalysisEventListener类中直接复制 @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { System.out.println("表头" + headMap); } // 读取完成之后做的事情 @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } } // 3.最终方法的调用 public class TestEasyExcel2 { public static void main(String[] args) { // 实现excel的读操作 String filename = "C:\\01.xls"; EasyExcel.read(filename,DemoData2.class,new ExcelListener()).sheet().doRead(); } // 创建一个方法,返回list集合 private static List<DemoData> getData(){ List<DemoData> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { DemoData data = new DemoData(); data.setSno(i); data.setSname("lucy" + i); list.add(data); } return list; } }
三、课程分类添加功能
引入依赖
使用代码生成器把课程分类的代码生成出来
// 将测试类(CodeGenerator)中的参数改为edu_subject表 strategy.setInclude("edu_subject"); // 运行
编写EduSubjectController
@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(); } }
创建实体类,和excel有对应关系
// 创建和excel对应的实体类 @Data public class SubjectData { @ExcelProperty(index = 0) private String oneSubjectName; @ExcelProperty(index = 1) private String twoSubjectName; }
service层
// 接口 public interface EduSubjectService extends IService<EduSubject> { // 添加课程分类 void saveSubject(MultipartFile file,EduSubjectService subjectService); } // 实现类 @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){ e.printStackTrace(); } } }
监听器
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(subjectService, 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(subjectService, subjectData.getTwoSubjectName(),pid); if (existTwoSubject == null){ existTwoSubject = new EduSubject(); existTwoSubject.setParentId(pid); existTwoSubject.setTitle(subjectData.getTwoSubjectName()); // 一级分类名称 subjectService.save(existTwoSubject); } } // 判断一级分类不能重复添加 private EduSubject existOneSubject(EduSubjectService subjectService,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(EduSubjectService subjectService,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) { } }
四、注意
// 1.添加注解@TableId
@ApiModelProperty(value = "课程类别ID")
@TableId(type = IdType.ID_WORKER_STR)
private String id;
// 2.时间自动填充
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;