文件上传的本质就是将文件通过IO流复制到服务器。文件上传是先上传到临时目录,然后再从临时目录复制到磁盘,目的是防止用户在上传途中网路中断、刷新页面导致文件上传失败,从而磁盘中产生大量垃圾文件和正常文件混在一起,所以只有文件上传成功后,才会从临时文件复制到磁盘,之后系统会自动删除临时文件。
前端HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>文件上传</title>
</head>
<body>
<h1>文件上传</h1>
<form action="/upload/file" id="touploadform" enctype="multipart/form-data" method="post">
<input name="dir" value="bbs">
<input id="file" name="file" onchange="toupload()" type="file" accept="image/*"/>
<script>
function toupload(){
document.getElementById("touploadform").submit();
}
file.addEventListener('change', async e => {
const file = e.target.files[0]
const flag = await isImage(file)
if (flag) {
alert('上传格式通过!')
} else {
alert('请上传正确的格式!')
}
})
// 判断是否为图片
async function isImage(file) {
return (await isGif(file)) || (await isPng(file)) || (await isJpg(file))
}
// 判断是否为 jpg 格式
async function isJpg(file) {
const res = await blobToString(file.slice(0, 3))
return res === 'FF D8 FF'
}
// 判断是否为 png 格式
async function isPng(file) {
const res = await blobToString(file.slice(0, 4))
return res === '89 50 4E 47'
}
// 判断是否为 gif 格式
async function isGif(file) {
const res = await blobToString(file.slice(0, 4))
return res === '47 49 46 38'
}
// 将文件转为十六进制字符串
async function blobToString(blob) {
return new Promise(resolve => {
const reader = new FileReader()
reader.onload = function () {
const res = reader.result
.split('') // 将读取结果分割为数组
.map(v => v.charCodeAt()) // 转为 Unicode 编码
.map(v => v.toString(16).toUpperCase()) // 转为十六进制,再转大写
.map(v => v.padStart(2, '0')) // 个位数补0
.join(' ') // 转为字符串
resolve(res)
}
reader.readAsBinaryString(blob) // 将文件读取为二进制字符串
})
}
</script>
<!-- <input type="submit" value="文件上传">-->
</form>
</body>
</html>
controller层
package com.kuangstuday.controller;
import com.kuangstuday.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;
import java.util.Map;
@Controller
public class UploadController {
@Autowired
private UploadService uploadService;
@PostMapping("/upload/file")
@ResponseBody
public Map<String,Object> upload(@RequestParam("file")MultipartFile multipartFile, HttpServletRequest request){
if (multipartFile.isEmpty()){
return null;
}
//1:获取用户指定的文件夹。问这个文件夹为什么要从页面上传递过来呐?
//原因是:做隔离,不同业务,不同的文件夹放在不同的目录中
String dir = request.getParameter("dir");
return uploadService.uploadImg(multipartFile,dir);
}
}
service层
package com.kuangstuday.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.HashMap;
import java.util.Map;
import java.util.UUID;
@Service
public class UploadService {
/**
* multipartFile 这个对象是springmvc提供的文件上传接受的类
* 它的底层自动会去和HttpServletRequest request 中的request.getInputStream()融合
* 从而达到文件上传的效果,也就是告诉你一个道理:
* 文件上传的原理是:request.getInputStream()
* @param multipartFile
* @param dir
* @return
*/
//从yml文件读取配置
@Value("${file.uploadFolder}")
private String uploadFolder;
@Value("${file.staticPath}")
private String staticPath;
public Map<String,Object> 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改写成:SD22424241-323432ms.jpg
//4.日期目录
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
String datePath = dateFormat.format(new Date());//日期目录:2022/11/08
//5.指定文件上传的目录
String servrepath= uploadFolder;
File targetFile = new File("E://tmp/"+dir,datePath);//生成一个最终目录:E://temp/bbs/2021/10/37
//如果文件夹不存在,则自动创建
if (!targetFile.exists())targetFile.mkdirs();//如果目录不存在递归创建:E://temp/bbs/2021/10/37
//6.指定文件上传以后的目录
//文件上传到服务器最终为:E://temp/bbs/2021/10/37/SD22424241-323432ms.jpg
File targetFileName = new File(targetFile,newFileName);
//7.文件上传到指定目录
multipartFile.transferTo(targetFileName);//将用户选择的aaa.jpg,上传到服务器E://temp/bbs/2021/10/37/SD22424241-323432ms.jpg
Map<String,Object> map = new HashMap<>();
map.put("url",staticPath+"/upimges/"+dir +"/"+ datePath +"/"+newFileName);
map.put("size",multipartFile.getSize());
map.put("ext",imgSuffix);
map.put("filename",realfilename);
map.put("rpath",dir+"/"+ datePath +"/"+newFileName);
return map;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
application.yml文件
server:
port: 8777
spring:
freemarker:
suffix: .html
cache: false
servlet:
multipart:
enabled: true
# 单个文件大小 默认1MB
max-file-size: 1MB
# 设置总上传的文件大小
max-request-size: 10MB
# 当文件达到多少时进行磁盘写入
file-size-threshold: 20MB
# 设置临时目录
location: E://data//temp
config层
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
/**方法一:这个就是springboot中springMvc让程序开发者去配置文件上传的额外的静态资源服务的配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/uploadimg/**").addResourceLocations("file:E://tmp//");
}
*registry.addResourceHandler("访问的路径").addResourceLocations("上传资源的路径");
*这个时候你把aaa.png文件上传到了E://tmp下,那么,你可以通过:http://localhost:8777/uploadimg/aaa.png访问到图片
*/
@Value("${file.staticPatternPath}")
private String staticPatternPath;
@Value("${file.uploadFolder}")
private String uploadFolder;
/**
* 方法二
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(staticPatternPath).addResourceLocations("file:"+uploadFolder);
}
}
application-dev.yml配置
# 本级配置
file:
staticPatternPath: /upimges/**
uploadFolder: E:/tmp/
application-prod.yml配置
#服务器配置
file:
staticPatternPath: /upimges/**
uploadFolder: /www/upload
application.yml配置激活dev
server:
port: 8777
spring:
freemarker:
suffix: .html
cache: false
profiles:
active: dev
servlet:
multipart:
enabled: true
# 单个文件大小 默认1MB
max-file-size: 1MB
# 设置总上传的文件大小
max-request-size: 10MB
# 当文件达到多少时进行磁盘写入
file-size-threshold: 20MB
# 设置临时目录
location: E://data//temp
在浏览器访问http://localhost:8777/upimges/bbs/2022/11/09/2454a5bf-29e2-42b7-a7d6-8b37b0903668.png