概述场景
文件上传,是程序开发中必须会使用到的一个功能,比如:
- 添加商品,用户头像,文章封面等需求
- 富文本编辑(插件文件上传)
文件上传的原理是什么?
我们为什么要实现文件上传,其实就要共享资源,大家都可以看你的在平台上上传的文件;其实就是把用户自己电脑中的文件,通过程序上传到服务器的过程。
总结一下:把用户的文件javaIO流复制到服务器上的过程,称之为文件上传。
用springboot实现文件上传
实现步骤
1.搭建一个springboot项目
2.准备一个页面上传的页面
在resources/templates/upload.html
server:
port: 8777
spring:
freemarker:
suffix: .html
cache: false
3、实现后台的文件上传
定义文件上传的service
package com.ygt.service;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* @Author ygt
* @Date 2022/9/21 18:50
* @Version 1.0
*/
@Service
public class UploadService {
/**
* MultipartFile 这个对象是springMvc提供的文件上传的接收的类,
* 他的底层会去和HttpServletRequest request 中的request.getInputStream()融合
* 从而达到文件上传的效果。也就是说:
* 文件上传的原理就是:request.getInputStream()
* @param multipartFile
* @param dir
* @return
*/
public String uploadImg(MultipartFile multipartFile,String dir){
//1.指定文件上传的目录
File targetFile = new File("D://tmp/" + dir);
try {
if(!targetFile.exists())targetFile.mkdirs();
//2.指定文件上传以后的目录
File targetFileName = new File(targetFile,"aaa.png");
//3.文件上传到指定的目录
multipartFile.transferTo(targetFileName);
return "ok";
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
}
}
定义一个文件上传的controller
package com.ygt.controller;
import com.ygt.service.UploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
/**
* @Author ygt
* @Date 2022/9/21 18:49
* @Version 1.0
*/
@Controller
public class UploadController {
@Autowired
private UploadService uploadService;
/**
* 文件上传的具体实现
* @param multipartFile
* @param request
* @return
*/
@PostMapping("/upload/file")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile multipartFile, HttpServletRequest request){
if(multipartFile.isEmpty()){
return "文件有误!!";
}
//1:获取用户指定的文件夹。问这个文件夹为什么要从页面上传过来呢?
//原因是:做隔离,不同的业务、不同的文件放在不同的目录中
String dir = request.getParameter("dir");
return uploadService.uploadImg(multipartFile,dir);
}
}
定义一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<h1>文件上传</h1>
<form action="/upload/file" enctype="multipart/form-data" method="post">
<input name="dir" value="bbs">
<input name="file" type="file">
<input type="submit" value="文件上传">
</form>
</body>
</html>
点击文件上传,在后台看到如下代码:
springMvc中对文件上传提供了包装对象:MultipartFile 原理如下:
- 通过MultipartFile 我们可以很清楚的看到,文件已经被服务器接收
- 但是会产生一个临时目录,这个目录是可以去配置的
文件上传不会直接上传至实际目录,它一定要经过一个临时目录的中转以后,才会上传到真实目录,其作用为:
- 防止上传出现网络断开,或者用户上传直接刷新或者取消。因为如果用户的网络断开或者取消,就造成大量的垃圾文件
- 保证真实目录上传的文件一定是有效的
- 文件上传的大小:可以进行配置的
- 文件上传的类型:也是可以进行配置的
server:
port: 8777
spring:
freemarker:
suffix: .html
cache: false
servlet:
multipart:
enabled: true
# 单个文件大小 默认1M
max-file-size: 1MB
# 设置总上传文件的数据大小
max-request-size: 10MB
# 当文件达到多少时进行磁盘写入
file-size-threshold: 20MB
# 设置临时目录
location: D://tmp//tempxxx
4、指定文件上传的目录
- 配置springboot静态资源存储服务,将上传的文件放入指定的目录中
package com.ygt.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* @Author ygt
* @Date 2022/9/21 18:50
* @Version 1.0
*/
@Service
public class UploadService {
@Value("${file.uploadFolder}")
private String uploadFolder;
@Value("${file.staticPath}")
private String staticPath;
/**
* MultipartFile 这个对象是springMvc提供的文件上传的接收的类,
* 他的底层会去和HttpServletRequest request 中的request.getInputStream()融合
* 从而达到文件上传的效果。也就是说:
* 文件上传的原理就是:request.getInputStream()
* @param multipartFile
* @param dir
* @return
*/
public String uploadImg(MultipartFile multipartFile,String dir){
try {
String realfilename = multipartFile.getOriginalFilename(); // 上传的文件:aaa.jpg
//2.截取文件名的后缀
String imgSuffix = realfilename.substring(realfilename.lastIndexOf(".")); // 拿到:.jpg
//3.生成唯一的文件名,不能用中文命名,统一用英文名命名
String newFilename = UUID.randomUUID().toString() + imgSuffix; // 将aaa.jpg改写成:SDJFSDK3424.jpg
//4.日期目录
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
String datePath = dateFormat.format(new Date()); // 日期目录:2022/9/25
//5.指定文件上传以后的目录
String serverpath = uploadFolder; //这不是tomcat服务目录,别人不认识
File targetPath = new File(serverpath+dir,datePath); //生成一个最终目录:D://tmp/avatar/2022/9/25
if(!targetPath.exists())targetPath.mkdirs(); //如果目录不存在:D://tmp/avatar/2022/9/25 递归创建
//6.指定文件上传以后的服务器的完整的文件名
File targetFileName = new File(targetPath,newFilename); //文件上传以后在服务器上最终文件名和目录是:D://tmp/avatar/2022/9/25/SDJFSDK3424.jpg
//7.文件上传到指定的目录
multipartFile.transferTo(targetFileName); //将用户选择的aaa.jpg上传到D://tmp/avatar/2022/9/25/SDJFSDK3424.jpg
// 可访问的路径要返回页面
// http://localhost:8777/avatar/2022/9/25/SDJFSDK3424.jpg
String filename = dir+"/"+datePath+"/"+newFilename;
return staticPath+"/"+filename;
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
}
}
5、通过http请求服务资源
springboot如何指定任意目录作为我们资源的访问目录?
springboot有一个目录:static这个目录其实就是静态资源目录,这个目录下面的文件是是可以通过http直接访问的。但是程序化一般是打成jar包的,我们没办法把文件写入到static下,所以springboot提供静态资源目录的额外的映射机制,就是静态资源服务映射。就类似于:nginx的静态资源映射。
package com.ygt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author ygt
* @Date 2022/9/29 1:04
* @Version 1.0
*/
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Value("${file.staticPatternPath}")
private String staticPatternPath;
@Value("${file.uploadFolder}")
private String uploadFolder;
// 这个就是springboot中springMvc让程序开发者去配置文件上传的额外的静态资源服务的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(staticPatternPath).addResourceLocations("file:"+uploadFolder);
// 跳转到出文件名前缀需要加上http://localhost:8777/uploadimg/
}
}
核心代码分析:
registry.addResourceHandler("访问的路径").addResourceLocations("上传资源的路径");
registry.addResourceHandler("/uploadimg").addResourceLocations("file:D://tmp");
这个时候你把:aaa.png文件上传到了D://tmp下,那么:你可以通过:http:localhost:8777/uploadimg/aaa.png
环境隔离配置:
application-dev.yml
# 本机配置
file:
staticPath: http://localhost:8777
staticPatternPath: /upimages/**
uploadFolder: D:/tmp/
application-prod.yml
# 服务器配置
file:
staticPath: https://www.kuangstudy.com
staticPatternPath: /upimages/**
uploadFolder: /www/upload/
application.yml激活环境
server:
port: 8777
spring:
freemarker:
suffix: .html
cache: false
profiles:
active: dev
servlet:
multipart:
enabled: true
# 单个文件大小 默认1M
max-file-size: 5MB
# 设置总上传文件的数据大小
max-request-size: 10MB
# 当文件达到多少时进行磁盘写入
file-size-threshold: 20MB
# 设置临时目录
location: D://tmp//tempxxx
6、对文件上传的思考和优化和控制
- 比如文件的格式
- 比如文件的大小
- 比如文件的重命名
- 比如文件的目录分类
以上就是我关于使用阿里云OSS实现文件上传的全部思路了,希望这篇文章能对屏幕前的你有所帮助。