P146 案例-文件上传-简介
文件上传,将本地图片、视频等文件上传到服务器,供其他用户浏览下载的过程。
文件上传前端页面三要素:
如果前端表单的编码格式选择的默认编码方式x-www.form-urlencoded,则提交的文件仅仅是文件名,文件内容没有提交到服务端,所以表单编码方式应该选择multipart/form-data,不然不能提交真正的文件内容。
图片、音频上传后文件内容可能是乱码。
服务端如何接收上传文件呢?
例如:springboot当中提供了multipartfile这个api,利用这个api可以接收上传文件,注意保证参数名和前端属性名一致。
P147、148、149、150 案例-文件上传-本地存储-OSS存储
服务端接收后是临时文件,之后就清除了。
两种存储方案:
1、本地存储
将文件存储在本地服务器磁盘中,multipartfile提供了保存磁盘的方法transferto。
下面是使用案例:
@PostMapping("/upload")
public Result upload(String name, Integer age, MultipartFile image) throws Exception{
log.info("文件上传:{}, {}, {}",name, age, image);
// 获取原始文件名
String originalFilename = image.getOriginalFilename();
image.transferTo(new File("E:\\mycodes\\ideaProject\\javaweb\\javaweb_project\\springboot-tlias\\springboot-tlias\\src\\main\\resources\\img\\"+originalFilename));
return Result.success();
}
对上述操作优化:如果直接采用原始名存储,则会造成存在相同名称的文件会相互冲突,后来的文件会覆盖掉前面的文件,因此需要保证每一个文件是唯一的,如何构造唯一的文件名?
时间戳也不一定,采用uuid解决。
uuid:通用唯一识别码,长度固定的字符串且不重复,
// uuid示例
// java.util中有UUID工具类
@Test
public void testUuid(){
for (int i = 0; i < 100; i++){
String s = UUID.randomUUID().toString();
System.out.println(s);
}
}
输出如下所示:输出32位,4个-,总长36位,
但是uuid不存在文件名后缀,所以需要添加文件扩展名,如何获取文件扩展名?截取.之后的
因此使用UUID改进后的方法如下所示:
@PostMapping("/upload")
public Result upload(String name, Integer age, MultipartFile image) throws Exception{
log.info("文件上传:{}, {}, {}",name, age, image);
// 获取原始文件名
String originalFilename = image.getOriginalFilename();
// 获取文件扩展名,即.之后的
int index = originalFilename.lastIndexOf(".");
// 从index截取到尾部获得扩展名
String extname = originalFilename.substring(index);
String newFileName = UUID.randomUUID().toString() + extname;
log.info("获取到新的文件名:{}", newFileName);
image.transferTo(new File("E:\\mycodes\\ideaProject\\javaweb\\javaweb_project\\springboot-tlias\\springboot-tlias\\src\\main\\resources\\img\\"+newFileName));
return Result.success();
}
能够成功以UUID保存文件
默认情况下上传文件不能超过超过1M,否则会报错,如果需要上传大文件,可以进行如下配置:在Properties中
# 配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
# 配置单个请求最大上传大小,一次请求可上传多个 文件
spring.servlet.multipart.max-request-size=100MB
存储本地磁盘缺点:
-
无法直接访问本地
-
服务器磁盘满了不方便扩容
-
如果磁盘坏了则会造成损失
multipartfile常用方法:
-
String getOriginalFilename()获取原始文件名
-
void transferTo(File dest)将接收的文件转存到磁盘文件中
-
long getSize()获取文件的大小,单位为字节
-
byte[] getBytes()获取文件内容的字节数组
-
InputStream getInputStream()获取接收到的文件内容的输入流
不用本地存储,常用
-
项目组自己搭建的文件存储服务,例如分布式服务或集群
-
使用云服务,方便可靠
2、云存储
阿里云,“云”即互联网,云服务即通过互联网提供各种各样的服务。
OSS对象存储服务,object storage service,可通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。
您可以进行以下操作:
-
创建一个或者多个存储空间,向每个存储空间中添加一个或多个文件。
-
通过获取已上传文件的地址进行文件的分享和下载。
-
通过修改存储空间或文件的属性或元信息来设置相应的访问权限。
-
在阿里云管理控制台执行基本和高级OSS任务。
-
使用阿里云开发工具包或直接在应用程序中进行RESTful API调用执行基本和高级OSS任务
学习如何使用云服务。
第三方服务通用思路:
-
准备工作
-
参照官方SDK编写入门程序
-
集成使用
bucket是阿里云OSS中的存储空间,即用户存储对象的容器,所有的对象都必须隶属于某个存储空间。使用步骤
可在官方使用文档中进行查看手册,创建好bucket之后,在项目中引入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
编写测试单元进行测试,参照文件流上传示例,下面是核心操作
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = new FileInputStream(filePath);
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
} catch (OSSException oe) {
完整如下:可以上传成功,并且上传的每个文件还有可访问的url。
@Test
public void testOss(){
String endpoint = "oss-cn-beijing.aliyuncs.com"; // 查询阿里云bucket
String accessKeyId = "";
String accessKeySecret = "";
String bucketName = "hemastudy";
String objectName = "0001.jpg";
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
String filePath= "E:\\download\\3bi4.jpg";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = new FileInputStream(filePath);
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
} 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 (Exception 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中。
-
接收上传的图片
-
将图片存储起来吗
-
返回图片访问的url
使用标签展示图片。
实现阿里云上传的工具类,调用上传方法返回url。可将该类加到IOC容器管理,添加注解@Component。注意自己修改endpoint要加上https://
/**
* 阿里云 OSS 工具类
*/
public class AliOSSUtils {
private String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
private String accessKeyId = "LTAI4GCH1vX6DKqJWxd6nEuW";
private String accessKeySecret = "yBshYweHOpqDuhCArrVHwIiBKpyqSL";
private String bucketName = "web-tlias";
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
使用方法:
@PostMapping("/upload1")
public Result upload(MultipartFile image) throws IOException {
String url = aliOSSUtils.upload(image);
return Result.success();
}
小结:
-
了解文件上传,文件上传前端页面三要素
-
服务端接收文件Multipartfile
-
文件存储
P151、P152 案例-修改员工-查询回显
修改员工两步:先要依据点击的记录请求服务端返回数据用于页面回显展示,保存需要更新数据。
查询回显:按照id进行查询即可。
修改员工采用动态SQL,使用判断是否需要更新。
P153 案例-配置文件-参数配置化
springboot的配置文件。
问题分析,怎样将一些参数的设置进行集中管理?
配置文件一般只有一个,可以考虑将参数定义在配置文件中,kye=value的形式。
如果在类中需要使用这些参数的话,如何读取到?方式是通过IO流来读取配置文件,解析配置文件中的值,然后为类中成员赋值。
上述方法在springboot中已经解决了,提供了一个注解@value,用于外部配置的属性注入,具体用法@value("${配置文件中的key}")
。
多行编辑,alt+鼠标左键,
例如:
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
P154 案例-配置文件-yml配置文件
springboot中提供了多种属性配置方式:
-
application.properties:key = value的形式
-
application.yml
-
application.yaml ,两者配置形式相同,配置形式为key:value
例如设置内嵌的tomcat端口号修改为9000
server:
port: 9000
address:
下面是常见的配置文件格式对比:推荐使用yml格式的配置文件
yml格式的基本语法:
-
大小写敏感
-
数值前边必须有空格,作为分隔符
-
使用缩进表示层级关系,缩进时不允许使用Tab键,只能用空格
-
缩进的空格数不重要,相同层级的元素左侧对齐
-
#表示注释,
yml中常见的数据格式:
-
对象/Map集合,例如
user:
name: xxx
age: 12
password: xxx
- 数组/List/Set集合
```YAML
hobby:
- java
- C
- game
- sport
使用yml配置文件同样可以运行。
P155 案例-配置文件-@Configuration
如果采用value注解注入配置文件中的参数,当比较多的时候则会比较繁琐、臃肿。
springboot中有解决,可以自动将配置文件中的值自动注入到需要的类成员中,注意key需要对应,并且为实体类属性提供get、set方法,并将实体类交给IOC容器管理,如果有前缀则要添加,则前面的aliossutils可以修改为如下形式:
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOssproperties {
private String endpoint;
private String accessKeyId;
private String ccessKeySecret;
private String ucketName;
}
// 在使用的 时候直接注入bean对象即可
需要引入依赖:自动识别被configurationproperties标识的对象
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
@ConfigurationProperties与@Value的区别: