1. ElementUI中文件上传
1.1 UI入门案例
<el-upload
class="upload-demo"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<script>
//对外声明组件属性/方法等参数.要被根组件调用
export default {
data(){
return {
tableData: [
{id:100, name:"黑熊精", age: 3000, sex:"男"},
{id:100, name:"黑旋风", age: 3000, sex:"男"},
{id:100, name:"黑心肠", age: 3000, sex:"男"},
{id:100, name:"黑桃A", age: 3000, sex:"男"}
],
dialogVisible: true,
fileList: [{name: 'food.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}, {name: 'food2.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}]
}
},
methods: {
handleSizeChange(size){
alert("当前条数:"+size)
},
handleCurrentChange(current){
alert("当前页数:"+current)
},
handleRemove(file, fileList) {
console.log("点击删除按钮时,触发JS")
console.log(file, fileList);
},
handlePreview(file) {
console.log("点击url地址之后,触发!!!!!")
console.log(file);
}
}
}
</script>
2. 文件上传入门案例
2.1 UI页面JS分析
<!--
了解:
action: 文件上传提交的地址
name: 默认文件上传的名称 file
-->
<el-upload class="upload-demo" :action="uploadUrl" :on-preview="handlePreview" :on-remove="handleRemove"
:on-success="handleSuccess" list-type="picture" multiple drag>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
2.2 业务接口说明
- 请求路径: http://localhost:8091/file/upload
- 请求类型: post
- 请求参数:
参数名称 | 参数说明 | 备注 |
---|---|---|
file | 文件上传的参数名称 | file中携带的是二进制信息 |
- 返回值结果:
参数名称 | 参数说明 | 备注 |
---|---|---|
status | 状态信息 | 200表示服务器请求成功 201表示服务器异常 |
msg | 服务器返回的提示信息 | 可以为null |
data | 服务器返回的业务数据 | 返回ImageVO对象 |
- ImageVO对象说明
参数名称 | 参数类型 | 参数说明 | 备注 |
---|---|---|---|
virtualPath | String | 图片实际路径 不包含磁盘信息 | 例如: 2021/11/11/a.jpg 不需要写磁盘地址 |
urlPath | String | 图片url访问地址 | http://image.jt.com/2021/11/11/a.jpg 需要指定域名地址 |
fileName | String | 文件上传后的文件名称 | UUID.type |
2.3 编辑ImageVO
说明: 为了封装文件上传之后的结果,需要封装VO对象 格式如下
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO implements Serializable {
private String virtualPath; //虚拟地址,不包含磁盘地址
private String urlPath; //URL地址信息.
private String fileName; //文件名称
}
2.4 入门案例实现
package com.jt.controller;
import com.jt.vo.SysResult;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
/**
* 业务: 实现文件上传
* url: /file/upload
* 请求类型: POST
* 参数: file
* 返回值: SysResult(imageVO)
* 高级API:MultipartFile 自动维护了缓存流/自动开关
*
* 文件上传步骤:
* 1.获取文件名称.
* 2.准备上传文件的目录
* 3.封装文件全路径 目录/文件名称
* 4.实现文件上传
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
//1.获取文件名称 a.jpg
String fileName = file.getOriginalFilename();
//2.准备文件目录
String fileDir = "G:/images/";
//2.1 判断目录是否存在
File dir = new File(fileDir);
if(!dir.exists()){
//如果目录不存在,则创建多级目录
dir.mkdirs();
}
//3.准备文件全路径
String localPath = fileDir + fileName;
//4.实现文件输出
file.transferTo(new File(localPath));
System.out.println("文件上传成功!!!!");
return SysResult.success();
}
}
3. 图片上传业务实现
3.1 文件上传的注入事项
- 控制文件上传的类型(后台为主)
- 控制恶意程序的上传 通过宽度和高度
- 为了提高检索的速度,应该分目录存储.
- 动态生成文件名称 UUID,防止文件重名.
- 实现文件上传 注意路径.
3.2 正则表达式
3.2.1 正则说明
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
3.2.2 语法介绍
1.标识开头/结尾/匹配多次
regex: abc 标识只能匹配固定的字符abc
regex: abc* 标识匹配 ab,c可以任意次.
regex: abc? 标识匹配 ab,c可以1次或者0次.
2.匹配确定次
regex: c{3} c只能出现3次
regex: c{3,} c出现>=3次
regex: c{3,10} c出现>=3 <=10次
regex: .* 匹配所有字符.
3.匹配固定字符
a[ab] 匹配2个字符 第一个字符必须为a, 第二个字符 必须a 或者b
[a-z][0-9] 第一个字符是a-z 第二个字符必须是0-9
5.分组匹配
demo: (png|jpg|gif) 字符要么是png,要么是ipg 要么是gif.
3.3 编辑FileController
@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
@Autowired
private FileService fileService;
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
ImageVO imageVO = fileService.upload(file);
if(imageVO == null){//说明业务执行有误
return SysResult.fail();
}
return SysResult.success(imageVO);
}
/**
* 业务: 实现文件上传
* url: /file/upload
* 请求类型: POST
* 参数: file
* 返回值: SysResult(imageVO)
* 高级API:MultipartFile 自动维护了缓存流/自动开关
*
* 文件上传步骤:
* 1.获取文件名称.
* 2.准备上传文件的目录
* 3.封装文件全路径 目录/文件名称
* 4.实现文件上传
*/
/*@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
//1.获取文件名称 a.jpg
String fileName = file.getOriginalFilename();
//2.准备文件目录
String fileDir = "G:/images/";
//2.1 判断目录是否存在
File dir = new File(fileDir);
if(!dir.exists()){
//如果目录不存在,则创建多级目录
dir.mkdirs();
}
//3.准备文件全路径
String localPath = fileDir + fileName;
//4.实现文件输出
file.transferTo(new File(localPath));
System.out.println("文件上传成功!!!!");
return SysResult.success();
}*/
}
3.4 编辑FileService
package com.jt.service;
import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
@Service
public class FileServiceImpl implements FileService{
private String localDir = "G:/images";
/**
* 完成校验:
* 1.校验是否为图片
* 2.木马.exe.jpg 判断是否满足图片固有属性 高度/宽度
* 3.为了提高查询效率,要求分目录存储.
* 3.1 按照后缀名分配 jpg,png,gif 效率提升不能满足要求
* 3.2 按照日期分 yyyy/MM/dd/HH 可以
* 3.3 商品分类 出现分布不均现象.
* 3.4 根据名称hash 之后截串
* demo: hash(a)=qw|er|as|dg/a.jpg
* 弊端: hash码可能出现分布不均的现象.
* 4.防止文件重名 使用uuid代替名称
* @param file
* @return
*/
@Override
public ImageVO upload(MultipartFile file) {
//1.获取图片名称 demo: abc.jpg abc.JPG
String fileName = file.getOriginalFilename();
//bug说明: 由于windows系统不区分大小写,所以将字母全部转化为小写
fileName = fileName.toLowerCase();
//利用正则判断是否为图片.
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
//如果不是图片,则返回null
return null;
}
//2.检查文件是否为恶意程序.
try {
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
if(width == 0 || height == 0){
//说明文件不是图片.
return null;
}
//3.根据时间实现目录的创建 时间--yyyy/MM/dd
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
.format(new Date());
// "G:/images/2021/11/11
String localDirPath = localDir + dateDir;
//创建目录
File dirFile = new File(localDirPath);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//4. 使用uuid替换文件名称 唯一:系统内部唯一
String uuid = UUID.randomUUID().toString()
.replace("-","");
//截取文件的后缀 aa.bb.cc.jpg
int index = fileName.lastIndexOf(".");
//获取类型 .jpg
String fileType = fileName.substring(index);
String newFileName = uuid + fileType;
//5.实现文件上传操作 目录/文件名称
String realFilePath = localDirPath + newFileName;
file.transferTo(new File(realFilePath));
System.out.println("文件上传成功!!!");
/*
6.封装返回值
* 封装虚拟路径 在各个系统之间可以灵活切换,只保存动态变化的目录
* path = 时间/uuid.type
*/
String virtualPath = dateDir + newFileName;
String url = "https://img14.360buyimg.com/n0/jfs/t1/157402/13/13529/158789/60517f36E2e8f9939/958bdb78df7c145f.jpg";
return new ImageVO(virtualPath,url,newFileName);
} catch (IOException e) {
e.printStackTrace();
return null; //表示程序有问题
}
}
}
3.5 页面效果展现