外贸网站都有哪些,运营网站是多少,企业做哪个网站好,聊城网站建设基本流程前一节已经实现了员工信息的条件分页查询以及删除操作。 这一节继续完成新增员工、文件上传、修改员工、配置文件的功能。 目录 新增员工文件上传简介本地存储阿里云OSS介绍与入门项目集成阿里云(难点) 修改员工查询回显修改员工 配置文件参数配置化(Value)yml配置文件Configur…前一节已经实现了员工信息的条件分页查询以及删除操作。 这一节继续完成新增员工、文件上传、修改员工、配置文件的功能。 目录 新增员工文件上传简介本地存储阿里云OSS介绍与入门项目集成阿里云(难点) 修改员工查询回显修改员工 配置文件参数配置化(Value)yml配置文件ConfigurationProperties 新增员工 在新增用户时我们需要保存用户的基本信息并且还需要上传的员工的图片目前我们先完成第一步操作保存用户的基本信息。
基本信息
请求路径/emps
请求方式POST
接口描述该接口用于添加员工的信息请求参数样例
{image: https://web-framework.oss-cnhangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg,username: linpingzhi,name: 林平之,gender: 1,job: 1,entrydate: 2022-09-18,deptId: 1
}其中username name gender 为必须参数 其他可缺省怎么在controller中接收json格式的请求参数RequestBody //把前端传递的json数据填充到实体类中
忘记了就回顾一下SpringBoot Web请求响应
EmpController //新增员工PostMappingpublic Result save(RequestBody Emp emp){//记录日志log.info(新增员工, emp:{},emp);//调用业务层新增功能empService.save(emp);//响应return Result.success();}EmpServiceImpl Overridepublic void save(Emp emp) {//补全数据emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());//调用添加方法empMapper.insert(emp);}EmpMapper //新增员工Insert(insert into emp (username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime});)void insert(Emp emp);文件上传
简介
在我们完成的新增员工功能中还存在一个问题没有头像(图片缺失) 首先我们先对文件上传做一个整体的介绍接着再学习文件上传的本地存储方式最后学习云存储方式。想要完成文件上传这个功能需要涉及到两个部分
前端程序服务端程序
我们先来看看在前端程序中要完成哪些代码
form action/upload methodpost enctypemultipart/form-data姓名: input typetext nameusernamebr年龄: input typetext nameagebr头像: input typefile nameimagebrinput typesubmit value提交
/form上传文件的原始form表单要求表单必须具备以下三点上传文件页面三要素 表单必须有file域用于选择要上传的文件 input typefile nameimage/表单提交方式必须为POST 通常上传的文件会比较大所以需要使用 POST 提交方式 表单的编码类型enctype必须要设置为multipart/form-data 普通默认的编码格式是不适合传输大型的二进制数据的所以在文件上传时表单的编码格式必须设置为multipart/form-data
写一个前端页面来验证一下
!DOCTYPE html
html langen
headmeta charsetUTF-8title上传文件/title
/head
bodyform action/upload methodpost enctypemultipart/form-data姓名: input typetext nameusernamebr年龄: input typetext nameagebr头像: input typefile nameimagebrinput typesubmit value提交/form/body
/html删除form表单中enctype属性值会是什么情况 点击提交按钮进入到开发者模式观察 再来验证设置form表单中enctype属性值为multipart/form-data会是什么情况 那我们的后端程序又是如何实现的呢 首先在服务端定义这么一个controller用来进行文件上传然后在controller当中定义一个方法来处理/upload 请求(请求路径随便定义, 前后端统一即可) 在定义的方法中接收提交过来的数据 方法中的形参名和请求参数的名字保持一致 用户名String name年龄 Integer age文件 MultipartFile image Spring中提供了一个APIMultipartFile使用这个API就可以来接收到上传的文件 问题如果表单项的名字和方法中形参名不一致该怎么办 解决使用RequestParam注解进行参数绑定 public Result upload(String username,Integer age, RequestParam(image) MultipartFile file)UploadController
Slf4j
RestController
public class UploadController {PostMapping(/upload)public Result upload(String username, Integer age, MultipartFileimage) {log.info(文件上传{},{},{},username,age,image);return Result.success();}
}后端程序编写完成之后打个断点以debug方式启动SpringBoot项目 打开浏览器输入http://localhost:8080/upload.html 录入数据并提交 通过后端程序控制台可以看到上传的文件是存放在一个临时目录 打开临时目录可以看到以下内容 表单提交的三项数据(姓名、年龄、文件)分别存储在不同的临时文件中 当我们程序运行完毕之后这个临时文件会自动删除。 所以我们如果想要实现文件上传需要将这个临时文件要转存到我们的磁盘目录中。 本地存储
下面呢我们就需要完成将上传的文件保存在服务器的本地磁盘上。
代码实现
在服务器本地磁盘上创建images目录用来存储上传的文件例E盘创建images目录使用MultipartFile类提供的API方法把临时文件转存到本地磁盘目录下 MultipartFile 常见方法 String getOriginalFilename(); //获取原始文件名void transferTo(File dest); //将接收的文件转存到磁盘文件中long getSize(); //获取文件的大小单位字节byte[] getBytes(); //获取文件内容的字节数组InputStream getInputStream(); //获取接收到的文件内容的输入流 UploadController
Slf4j
RestController
public class UploadController {PostMapping(/upload)public Result upload(String username, Integer age, MultipartFileimage) throws IOException {log.info(文件上传{},{},{},username,age,image);//获取原始文件名String originalFilename image.getOriginalFilename();//将文件存储在服务器的磁盘目录image.transferTo(new File(E:/images/originalFilename));return Result.success();}
}利用postman测试注意请求参数名和controller方法形参名保持一致 通过postman测试我们发现文件上传是没有问题的。但是由于我们是使用原始文件名作为所上传文件 的存储名字当我们再次上传一个名为1.jpg文件时发现会把之前已经上传成功的文件覆盖掉。
解决方案保证每次上传文件时文件名都唯一的使用UUID获取随机文件名
Slf4j
RestController
public class UploadController {PostMapping(/upload)public Result upload(String username, Integer age, MultipartFileimage) throws IOException {log.info(文件上传{},{},{},username,age,image);//获取原始文件名String originalFilename image.getOriginalFilename();//构建新的文件名String extname originalFilename.substring(originalFilename.lastIndexOf(.));//文件扩展名String newFileName UUID.randomUUID().toString()extname;//随机名文件扩展名//将文件存储在服务器的磁盘目录image.transferTo(new File(E:/images/newFileName));return Result.success();}
}在解决了文件名唯一性的问题后我们再次上传一个较大的文件(超出1M)时发现后端程序报错 因为在SpringBoot中文件上传时默认单个文件最大大小为1M 那么如果需要上传大文件可以在application.properties进行如下配置
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size10MB#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size100MB到此我们文件上传的本地存储方式已完成了。但是这种本地存储方式还存在问题 如果直接存储在服务器的磁盘目录中存在以下缺点
不安全磁盘如果损坏所有的文件就会丢失容量有限如果存储大量的图片磁盘空间有限(磁盘不可能无限制扩容)无法直接访问
为了解决上述问题呢通常有两种解决方案
自己搭建存储服务器如fastDFS 、MinIO使用现成的云服务如阿里云腾讯云华为云 阿里云OSS介绍与入门 阿里云是国内最大的云服务提供商。当我们在项目开发时需要用到某个或某些服务就不需要自己来开发了可以直接使用阿里云提供好的这些现成服务就可以了,如阿里云提供的短信服务。这样就降低了我们项目的开发难度同时也提高了项目的开发效率。
阿里云对象存储OSSObject Storage Service是一款海量、安全、低成本、高可靠的云存储服务。使用OSS您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。 在我们使用了阿里云OSS对象存储服务之后我们的项目当中如果涉及到文件上传这样的业务在前端进行文件上传并请求到服务端时在服务器本地磁盘当中就不需要再来存储文件了。我们直接将接收到的文件上传到oss由 oss帮我们存储和管理同时阿里云的oss存储服务还保障了我们所存储内容的安全可靠。 我们主要学习的是如何在项目当中来使用云服务完成具体的业务功能。无论使用什么样的云服务阿里云也好腾讯云、华为云也罢操作的思路都是一样的 SDKSoftware Development Kit 的缩写软件开发工具包包括辅助软件开发的依赖jar包、代码示例等都可以叫做SDK。 简单说sdk中包含了我们使用第三方云服务时所需要的依赖以及一些示例代码。我们可以参照sdk所提供的示例代码就可以完成入门程序。 介绍一下我们当前要使用的阿里云oss对象存储服务具体的使用步骤。 Bucket用户用于存储对象Object就是文件的容器所有的对象都必须隶属于某个存储空间。 注册阿里云账户注册完成后需要实名认证后就可以登录阿里云 如果是第一次访问还需要开通对象存储服务OSS。开通OSS服务之后就可以进入到阿里云对象存储的控制台。
点击左侧的 “Bucket列表”创建一个Bucket 阿里云oss 对象存储服务的准备工作我们已经完成了接下来我们就来完成第二步操作参照官方所提供的sdk示例来编写入门程序。在官方文档中找到 SDK 的示例代码 如果是在实际开发当中我们是需要从前往后仔细的去阅读这一份文档的。 参照官方提供的SDK改造一下即可实现文件上传功能上传前要按照Java配置访问凭证的流程获取AccessKey配置一下环境变量。
AccessKey public class Demo {public static void main(String[] args) throws Exception {// Endpoint以华北2北京为例其它Region请按实际情况填写。String endpoint https://oss-cn-beijing.aliyuncs.com;// 从环境变量中获取访问凭证。运行本代码示例之前请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();// 填写Bucket名称例如examplebucket。String bucketName web-tx-36;// 填写Object完整路径例如exampledir/exampleobject.txt代表目标文件传输到buceket时的保存路径。Object完整路径中不能包含Bucket名称。String objectName exampledir/中国梦.txt;// 填写本地文件的完整路径例如D:\\localpath\\examplefile.txt。// 如果未指定本地路径则默认从示例程序所属项目对应本地路径中上传文件流。String filePathE:\\images\\中国梦.txt;// 创建OSSClient实例。OSS ossClient new OSSClientBuilder().build(endpoint, credentialsProvider);try {InputStream inputStream new FileInputStream(filePath);// 创建PutObjectRequest对象。PutObjectRequest putObjectRequest newPutObjectRequest(bucketName, objectName, inputStream);// 设置该属性可以返回response。如果不设置则返回的response为空。putObjectRequest.setProcess(true);// 创建PutObject请求。PutObjectResult result ossClient.putObject(putObjectRequest);// 如果上传成功则返回200。System.out.println(result.getResponse().getStatusCode());} catch (OSSException oe) {System.out.println(Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.);System.out.println(Error Message: oe.getErrorMessage());System.out.println(Error Code: oe.getErrorCode());System.out.println(Request ID: oe.getRequestId());System.out.println(Host ID: oe.getHostId());} catch (ClientException ce) {System.out.println(Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.);System.out.println(Error Message: ce.getMessage());} finally {if (ossClient ! null) {ossClient.shutdown();}}}
}运行以上程序后会把本地的文件上传到阿里云OSS服务器上 项目集成阿里云(难点)
接下来我们就需要在案例当中集成oss对象存储服务来存储和管理案例中上传的图片。 OSS中的每一个文件都会分配一个访问的url通过这个url就可以访问到存储在阿里云上的图片。所以需要把url返回给前端这样前端就可以通过url获取到图像然后前端再调用新增员工api的时候存储的图片就是存储url。
基本信息
请求路径/upload
请求方式POST
接口描述上传图片接口请求参数格式multipart/form-data响应数据样例
{code: 1,msg: success,data: https://web-framework.oss-cnhangzhou.aliyuncs.com/2022-09-02-00-27-0400.jpg
}引入阿里云OSS上传文件工具类AliOSSUtils由官方的示例代码改造而来
Component
public class AliOSSUtils {// Endpoint以华北2北京为例其它Region请按实际情况填写。String endpoint https://oss-cn-beijing.aliyuncs.com;// 填写Bucket名称例如examplebucket。String bucketName web-tx-36;public String upload(MultipartFile multipartFile) throws Exception {// 获取上传的文件的输入流InputStream inputStream multipartFile.getInputStream();// 避免文件覆盖String originalFilename multipartFile.getOriginalFilename();String fileName UUID.randomUUID().toString() originalFilename.substring(originalFilename.lastIndexOf(.));//上传文件到 OSS// 从环境变量中获取访问凭证。运行本代码示例之前请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();// 创建OSSClient实例。OSS ossClient new OSSClientBuilder().build(endpoint, credentialsProvider);ossClient.putObject(bucketName, fileName, inputStream);//文件访问路径String url endpoint.split(//)[0] // bucketName . endpoint.split(//)[1] / fileName;// 关闭ossClientossClient.shutdown();return url;// 把上传到oss的路径返回}
}修改UploadController代码
Slf4j
RestController
public class UploadController {Autowiredprivate AliOSSUtils aliOSSUtils;PostMapping(/upload)public Result upload(String username, Integer age, MultipartFileimage) throws Exception {//调用阿里云OSS工具类将上传上来的文件存入阿里云String url aliOSSUtils.upload(image);//将图片上传完成后的url返回用于浏览器回显展示return Result.success(url);}
}使用postman测试 前端测试 新增员工添加头像后后端可以给前端返回图片url并显示在前端页面。 修改员工 在进行修改员工信息的时候我们首先先要根据员工的ID查询员工的信息用于页面回显展示然后用户 修改员工数据之后点击保存按钮就可以将修改的数据提交到服务端保存到数据库。 具体操作 为
根据ID查询员工信息保存修改的员工信息
查询回显
基本信息
请求路径/emps/{id}
请求方式GET
接口描述该接口用于根据主键ID查询员工的信息请求参数样例
/emps/1响应数据样例 {code: 1,msg: success,data: {id: 2,username: zhangwuji,password: 123456,name: 张无忌,gender: 1,image: https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg,job: 2,entrydate: 2015-01-01,deptId: 2,createTime: 2022-09-01T23:06:30,updateTime: 2022-09-02T00:29:04}}EmpController //根据id查询员工GetMapping(/{id})public Result getById(PathVariable Integer id){Emp emp empService.getById(id);return Result.success(emp);}EmpServiceImpl Overridepublic Emp getById(Integer id) {return empMapper.findById(id);}EmpMapper //根据ID查询员工信息Select(select * from emp where id #{id})Emp findById(Integer id);postman测试: 前端编辑界面可以返回员工数据 修改员工
当用户修改完数据之后点击保存按钮就需要将数据提交到服务端然后服务端需要将修改后的数据更新到数据库中。 由于可能只修改部分字段所以需要使用xml来写动态sql
基本信息
请求路径/emps
请求方式PUT
接口描述该接口用于修改员工的数据信息请求参数样例
{id: 1,image: https://web-framework.oss-cnhangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg,username: linpingzhi,name: 林平之,gender: 1,job: 1,entrydate: 2022-09-18,deptId: 1
}EmpController //修改员工PutMappingpublic Result update(RequestBody Emp emp){empService.update(emp);return Result.success();}EmpServiceImpl Overridepublic void update(Emp emp) {emp.setUpdateTime(LocalDateTime.now()); //更新修改时间为当前时间empMapper.update(emp);}EmpMapper //修改员工信息public void update(Emp emp);EmpMapper.xml
!--更新员工信息--update idupdateupdate empsetif testusername ! null and username ! username #{username},/ifif testpassword ! null and password ! password #{password},/ifif testname ! null and name ! name #{name},/ifif testgender ! nullgender #{gender},/ifif testimage ! null and image ! image #{image},/ifif testjob ! nulljob #{job},/ifif testentrydate ! nullentrydate #{entrydate},/ifif testdeptId ! nulldept_id #{deptId},/ifif testupdateTime ! nullupdate_time #{updateTime}/if/setwhere id #{id}/updatepostman测试: 前端测试 配置文件
员工管理的增删改查功能我们已开发完成但在我们所开发的程序中还一些小问题。
参数配置化(Value)
在我们之前编写的程序中进行文件上传时需要调用AliOSSUtils工具类将文件上传到阿里云OSS对象存储服务当中。而在调用工具类进行文件上传时需要一些参数 endpoint //阿里云OSS域名 bucketName //存储空间的名字 …
关于以上的这些阿里云相关配置信息我们是直接写死在java代码中了(硬编码)如果这些参数发生变化了就必须在源程序代码中改动这些参数并且将这些参数分散的定义在各个Java类当中我们要修改一个参数值我们就需要在众多的Java代码当中来定位到对应的位置。
为了解决以上分析的问题我们可以将参数配置在配置文件中。如下
#自定义的阿里云OSS配置信息
aliyun.oss.endpointhttps://oss-cn-hangzhou.aliyuncs.com
aliyun.oss.bucketNameweb-tlias
...springboot程序在启动时会默认读取application.properties配置文件而我们可以使用一个现成的注解Value获取配置文件中的数据。具体用法为
Value(${配置文件中的key})Component
public class AliOSSUtils {Value(${aliyun.oss.endpoint})private String endpoint;Value(${aliyun.oss.bucketName})private String bucketName;//省略其他代码...
}使用postman测试 yml配置文件
除了支持properties配置文件以外springboot还 支持另外一种类型的配置文件——yml格式的配置文件。
yml 格式的配置文件后缀名有两种yml 推荐、yaml格式都一样。 yml配置文件的基本语法
大小写敏感数值前边必须有空格作为分隔符使用缩进表示层级关系缩进时不允许使用Tab键只能用空格idea中会自动将Tab转换为空格缩进的空格数目不重要只要相同层级的元素左侧对齐即可#表示注释从这个字符一直到行尾都会被解析器忽略 yml文件中最为常见的两类数据格式
对象/Map集合
user:name: zhangsanage: 18password: 123456数组/List/Set集合
hobby: - java- game- sport熟悉完了yml文件的基本语法后我们修改下之前案例中使用的配置文件变更为application.yml配置方式
修改application.properties名字为_application.properties名字随便更换只要加载不到即可创建新的配置文件 application.yml
原有application.properties文件 新建的application.yml文件
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/tliasusername: rootpassword: 1234servlet:multipart:max-file-size: 10MBmax-request-size: 100MBmybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truealiyun:oss:endpoint: https://oss-cn-hangzhou.aliyuncs.comaccessKeyId: LTAI4GCH1vX6DKqJWxd6nEuWaccessKeySecret: yBshYweHOpqDuhCArrVHwIiBKpyqSLbucketName: web-397ConfigurationProperties
先来看一个场景分析下代码当中可能存在的问题 我们在application.properties或者application.yml中配置了阿里云OSS的四项参数之后如果java程序中需要这四项参数数据我们直接通过Value注解来进行注入。这种方式本身没有什么问题问题但是如果说需要注入的属性较多(例需要20多个参数数据)我们写起来就会比较繁琐。
Spring提供的简化方式套路 创建一个实现类且实体类中的属性名和配置文件当中key的名字一致 比如配置文件当中叫endpoints实体类当中的属性也得叫endpoints另外实体类当中的属性还需要提供 getter / setter方法 需要将实体类交给Spring的IOC容器管理成为IOC容器当中的bean对象 在实体类上添加ConfigurationProperties注解并通过prefix属性来指定配置参数项的前缀 实体类AliOSSProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*阿里云OSS相关配置*/
Data
Component
ConfigurationProperties(prefix aliyun.oss)
public class AliOSSProperties {//区域private String endpoint;//身份IDprivate String accessKeyId ;//身份密钥private String accessKeySecret ;//存储空间private String bucketName;
}完成上述操作后修改AliOSSUtils工具类注入配置参数实体类对象并将原来使用其属性值的地方改成由参数实体类对象获取属性值(getter函数)。
AliOSSUtils工具类
Component //当前类对象由Spring创建和管理
public class AliOSSUtils {//注入配置参数实体类对象Autowiredprivate AliOSSProperties aliOSSProperties;/*** 实现上传图片到OSS*/public String upload(MultipartFile multipartFile) throws IOException {// 获取上传的文件的输入流InputStream inputStream multipartFile.getInputStream();// 避免文件覆盖String originalFilename multipartFile.getOriginalFilename();String fileName UUID.randomUUID().toString() originalFilename.substring(originalFilename.lastIndexOf(.));//上传文件到 OSSOSS ossClient new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);//文件访问路径String url aliOSSProperties.getEndpoint().split(//)[0] // aliOSSProperties.getBucketName() . aliOSSProperties.getEndpoint().split(//)[1] / fileName;// 关闭ossClientossClient.shutdown();return url;// 把上传到oss的路径返回}}
}在我们添加上注解后idea窗口弹出红色警告 是告知我们未配置Spring Boot配置注解处理器还需要引入一个依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactId
/dependency在pom.xml中配置了这项依赖之后重新启动服务就会看到在properties或者是yml配置文件当中就会提示阿里云 OSS 相关的配置项。所以这项依赖它的作用就是会自动的识别被ConfigurationProperties注解标识的bean对象。 刚才的红色警告已经变成了一个灰色的提示提示我们需要重新运行springboot服务 区分一下ConfigurationProperties注解以及我们前面所介绍的另外一个Value注解
相同点都是用来注入外部配置的属性的。
不同点 Value注解只能一个一个的进行外部属性的注入。 ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中。
如果要注入的属性非常的多并且还想做到复用就可以定义这么一个bean对象。通过 configuration properties 批量的将外部的属性配置直接注入到 bean 对象的属性当中。在其他的类当中我要想获取到注入进来的属性我直接注入 bean 对象然后调用 get 方法就可以获取到对应的属性值了