目录
<<回到导览
文件上传
1.本地储存
1.1.文件上传预览
-
前端页面文件上传三要素
-
表单项 type= “file”
-
表单提交方式 post
-
表单的enctype属性multipart/form-data
-
-
前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上传文件</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> 姓名: <input type="text" name="username"><br> 年龄: <input type="text" name="age"><br> 头像: <input type="file" name="image"><br> <input type="submit" value="提交"> </form> </body> </html>
-
服务端代码
方法名字要和前端input元素的name相同
@Slf4j @RestController public class UploadController { @PostMapping("/upload") public Result upload(String username, Integer age, MultipartFile image) { log.info("文件上传,{},{},{}", username, age, image); return Result.success(); } }
-
查看文件上传
-
打断点调试
为什么要打断点调试呢?因为我们这里服务器端没有写保存数据的代码,如果不打断点调试,数据传给后端就会被丢弃,我们无法预览到
-
用端口访问html页面(localhost:8080/upload)
-
点击提交,自动转跳到idea
复制location地址,在文件管理器中浏览,可以看到三个临时文件(就是我们刚刚前端表单中input上传的三个数据)
当请求响应完毕,临时文件就会被删除
-
1.2.文件本地储存
-
代码
@Slf4j @RestController public class UploadController { @PostMapping("/upload") public Result upload(String username, Integer age, MultipartFile image) throws IOException { log.info("文件上传,{},{},{}", username, age, image); // 获取原始文件名 String originalFilename = image.getOriginalFilename(); // 将文件储存到D:\桌面 image.transferTo(new File("D:\\桌面\\" + originalFilename)); return Result.success(); } }
-
测试
打开桌面,发现图片保存成功
1.3.本地储存优化
文件名重复优化:
-
问题:用户上传的文件名可能相同,同一个文件也可能被多次上传,如果这样操作,后面上传文件的文件会覆盖原文件
-
解决方法:构建唯一的文件名(UUID),这个在阿玮老师的javaee课程中讲到过
@Slf4j @RestController public class UploadController { @PostMapping("/upload") public Result upload(String username, Integer age, MultipartFile image) throws IOException { log.info("文件上传,{},{},{}", username, age, image); // 获取原始文件名 String originalFilename = image.getOriginalFilename(); // 构建唯一的文件名 int index = originalFilename.lastIndexOf(".");// 获取最后.的位置 String extname = originalFilename.substring(index);// 文件的后缀名 String newFilename = UUID.randomUUID().toString() + extname; log.info("新的文件名,{}", newFilename); // 将文件储存到D:\桌面 image.transferTo(new File("D:\\桌面\\" + newFilename)); return Result.success(); } }
文件大小限制优化
-
问题:默认请求下,限制文件最大为1M
-
解决方法:配置请求大小限制
# 配置单个文件上传大小的限制 spring.servlet.multipart.max-file-size=10MB # 配置单个请求最大大小的限制 spring.servlet.multipart.max-request-size=12MB
2.阿里云OSS
阿里云官网:https://www.aliyun.com/
阿里云对象存储0SS (Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。
SDK: Software Development Kit 的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包)、代码示例等,都可以叫做SDK
Bucket:存储空间是用户用于存储对象(Object,就是文件)的容器,所有的对象都必须隶属于某个存储空间。
2.1.开通OSS
-
创建bucket
创建bucket时,不运行打开公共访问,我们可以创建完成后手动打开
2.2.获取秘钥
注意:自2023年7月5日起,只能在创建时提供秘钥,所以我们要在这里就把秘钥保存好,不然只能重新创建秘钥
2.3.编写入门程序
oss SDK:https://oss.console.aliyun.com/sdk
-
在Maven工程中使用OSS Java SDK,只需在
pom.xml
中加入相应依赖即可。以在中加入3.17.4版本的依赖为例:<!-- 阿里云OSS --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.4</version> </dependency>
如果使用的是Java 9及以上的版本,则需要添加以下JAXB相关依赖。
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency> <!-- no more than 2.3.3--> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.3</version> </dependency>
入门程序
import org.junit.jupiter.api.Test; import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import java.io.FileInputStream; import java.io.InputStream; public class AliOssTest { @Test public void testOss(){ // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 String accessKeyId = "---------------------"; String accessKeySecret = "-----------------------"; // 填写Bucket名称,例如examplebucket。 String bucketName = "-----------"; // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。 String objectName = "0001.jpg"; // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。 // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。 String filePath= "C:\\Users\\Administrator\\Pictures\\Saved Pictures\\10.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(); } } } }
官网上的配置有些繁琐,由于这里还没有学配置环境变量,这里就先使用老师这个入门程序样例
配置完毕,运行入门程序,图片上传成功
我们还可以通过url访问这张图片
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <img src="你oss图片的url"> </body> </html>
2.4.案例集成
-
改造的工具类(com/jia/utils/AliOSSUtils.java)
/** * 阿里云 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的路径返回 } }
-
使用工具上传图片并返回图片地址
@Slf4j @RestController public class UploadController { @Autowired private AliOSSUtils ossUtil; @PostMapping("/upload") public Result upload(MultipartFile image) throws IOException { log.info("文件上传,文件名:{}", image.getOriginalFilename()); // 调用阿里云OSS工具类进行文件上传 String url = ossUtil.upload(image); log.info("文件上传完成,文件访问参数的url:{}", url); return Result.success(url); } }
3.配置文件
3.1.参数配置化
在OSS章节,我们将OSS的参数配置到了utils/AliOSSUtils.java文件,我们可以集中配置这些文件,方便维护和管理
-
utils/AliOSSUtils.java
@Value注解通常用于外部配置的属性注入
@Component public class AliOSSUtils { @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 ; // ... }
-
application.properties
aliyun.oss.endpoint=https://oss-cn-chengdu.aliyuncs.com aliyun.oss.accessKeyId=LTAI5tFmqYKEvooT4xUTFsew aliyun.oss.accessKeySecret=P2nDkC30PnVIiqUTUCQrygyhczc6Bk aliyun.oss.bucketName=jiaqi123
3.2.yml配置文件
相比于properties配置文件,yml/yaml配置文件具有层次结构分明的特点(yaml配置文件和properties配置文件存放于同一目录下)。
-
基本语法
- 大小写敏感
- 数值前边必须有空格,作为分隔符
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
#
表示注释,从这个字符一直到行尾,都会被解析器忽略
-
示例
-
对象、Map集合
user: name: Tom age: 20 address: chengdu
-
数组、List集合、Set集合
hobby: -Tom -Jack -Bob
-
-
修改案例配置文件
-
修改前
spring.application.name=emp # 数据库配置 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis spring.datasource.username=root spring.datasource.password=123456 # mybatis日志输出到控制台 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # 驼峰命名自动映射开关 mybatis.configuration.map-underscore-to-camel-case=true # 配置文件上传大小 spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=12MB # 阿里云OSS aliyun.oss.endpoint=https://oss-cn-chengdu.aliyuncs.com aliyun.oss.accessKeyId=LTAI5tFmqYKEvooT4xUTFsew aliyun.oss.accessKeySecret=P2nDkC30PnVIiqUTUCQrygyhczc6Bk aliyun.oss.bucketName=jiaqi123
-
修改后
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: mysql://localhost:3306/mybatis username: root password: 123456 servlet: multipart: max-file-size: 10MB max-request-size: 12MB mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true aliyun: oss: endpoint: https://oss-cn-chengdu:aliyuncs:com accessKeyId: LTAI5tFmqYKEvooT4xUTFsew accessKeySecret: P2nDkC30PnVIiqUTUCQrygyhczc6Bk bucketName: jiaqi123
-
3.3.@ConfigurationProperties
每个使用配置项的地方都要用@Value指定配置值,极为不便,我们可以用@ConfigurationProperties简化@Value指定配置值
-
简化前
@Component public class AliOSSUtils { @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 ; // ... }
-
简化后
配置时,属性名要相同
@Data @Component @ConfigurationProperties(prefix = "aliyun.oss") public class AliOSSUtils { private String endpoint ; private String accessKeyId ; private String accessKeySecret ; private String bucketName ; // ... }