文件传输
SpringMVC
在不使用框架之前,原生的HttpServletRequest来接收上传的数据,文件是以二进制流传递到后端的,需要我们自己转换为File类。
而在SpringMVC中提供了MultipartFile工具类之后,简化了文件传输操作。
后端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//Post请求,传入MultiPartFile对象
//MultiPartFile对象封装了上传文件的信息,包括文件名、文件大小、文件类型、文件内容等
public R<String> upload(MultipartFile file){
//file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
//获取原始文件名
String originalFilename = file.getOriginalFilename();//Ex abc.jpg
//获取文件后缀,包含.,如.jpg
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//使用UUID重新生成文件名,防止文件名称重复造成文件覆盖
String fileName = UUID.randomUUID().toString() + suffix;
//创建一个目录对象,其中basePath是配置文件中配置的文件上传路径
//java中认为文件夹是一种特殊的文件,所以File对象既可以表示文件,也可以表示文件夹
File dir = new File(basePath);
//判断当前目录是否存在
if(!dir.exists()){
//目录不存在,需要创建,
//mkdirs()方法会创建多级目录,mkdir()方法只会创建一级目录
dir.mkdirs();
}
try {
//调用MultiPartFile对象的transferTo方法,将文件写入指定位置
file.transferTo(new File(basePath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return R.success(fileName);
}
这里涉及到MultiPartFile和File两个对象
MultiPartFile对象封装了上传文件的信息,包括文件名、文件大小、文件类型、文件内容等,用于传输。
File对象表示文件或文件夹,可以通过File对象对文件或文件夹进行实际操作。
对于下载,使用HttpServletResponse对象,通过输出流将文件写回浏览器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void download(String name, HttpServletResponse response){
try {
//输入流,通过输入流读取文件内容
FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
//输出流,通过输出流将文件写回浏览器
ServletOutputStream outputStream = response.getOutputStream();
//设置响应头,告诉浏览器文件类型
response.setContentType("image/jpeg");
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fileInputStream.read(bytes)) != -1){
//将读取到的内容写入输出流中
outputStream.write(bytes,0,len);
outputStream.flush();
}
//关闭资源
outputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
其中这段是流中的常见操作
对于这段代码,首先要明确,输入流和输出流都是字节流,所以需要一个字节数组来缓存读取的内容。
read(byte b[])方法,每次读取b.length个字节,返回值表示实际读取的字节数,当返回-1时,表示文件读取完毕。
在这里,每次读取1024个字节,将读取到的内容写入输出流中,len表示每次读取的字节数,bytes表示读取的内容,0表示从bytes的第0个位置开始读取。当不足1024个字节时,len的值就会小于1024,这时就不需要将bytes数组中的所有内容写入输出流中,只需要将bytes数组中的前len个字节写入输出流中即可
1
2
3
4
5
6
7
8
9
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fileInputStream.read(bytes)) != -1){
outputStream.write(bytes,0,len);
//刷新输出流,将缓存中的内容写入到浏览器中,
//如果不刷新,可能会出现文件下载不完整的情况,因为浏览器会缓存一部分内容,等到缓存满了之后才会将内容写入到浏览器中
//所以,为了保证文件下载完整,需要在每次写入内容之后刷新一下输出流
outputStream.flush();
}
前端
前端的代码看起来是比后端有意思点的
下面是ElementUI中的组件,用于文件上传
其中,action="/common/upload"
用于指定上传文件时将数据发送到的URL地址。show-file-list="false
将show-file-list属性设置为false,表示在上传文件后不显示上传的文件列表。on-success
和on-change
分别对应了成功上传文件时要调用的函数和在上传文件时要调用的函数ref="upload
用于在组件中添加ref属性,以便在Vue组件实例中引用组件。
组件中包含两个子元素:<img v-if="imageUrl" :src="imageUrl" class="avatar">
如果存在一个名为imageUrl的变量,则在组件中显示一个<img>
元素,该元素的src属性设置为imageUrl变量的值。在这种情况下,class=”avatar”用于自定义图像的样式。<i v-else class="el-icon-plus avatar-uploader-icon"></i>
如果不存在名为imageUrl的变量,则在组件中显示<i>
元素,该元素包含一个“+”符号,用于指示用户可以上传文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<el-upload
class="avatar-uploader"
action="/common/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:on-change="onChange"
ref="upload"
>
<img
v-if="imageUrl"
:src="imageUrl"
class="avatar"
></img>
<i
v-else
class="el-icon-plus avatar-uploader-icon"
></i>
</el-upload>
下面是调用的函数
onChange 在el-upload中,当用户点击确定,会调用这个方法,主要是作为文件判断,但是不上传文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16onChange (file) {
if(file){
const suffix = file.name.split('.')[1]
const size = file.size / 1024 / 1024 < 2
if(['png','jpeg','jpg'].indexOf(suffix) < 0){
this.$message.error('上传图片只支持 png、jpeg、jpg 格式!')
this.$refs.upload.clearFiles()
return false
}
if(!size){
this.$message.error('上传文件大小不能超过 2MB!')
return false
}
return file
}
},文件的上传是通过调用组件实例的submit方法来实现的。el-upload组件提供了一系列的钩子函数(hooks)来监听上传过程的不同阶段,例如before-upload、on-progress、on-success、on-error等。通过设置这些钩子函数,可以在上传过程中执行相应的操作。(不由我们控制)
handleAvatarSuccess用于回显,在成功上传后会调用
on-success
对应的函数1
2
3
4
5
6
7
8
9handleAvatarSuccess (response, file, fileList) {
// this.imageUrl = response.data
if(response.code === 0 && response.msg === '未登录'){
window.top.location.href = '/backend/page/login/login.html'
}else{
this.imageUrl = `/common/download?name=${response.data}`
this.ruleForm.image = response.data
}
},
在el-upload组件中,当文件上传成功后,**服务器会返回一个响应结果(response)**,通常包含上传成功后的文件名或文件URL等相关信息。在on-success事件处理函数中,可以通过获取服务器响应结果的方式来获取上传成功后的文件名或文件URL。
4. 所以,在上传文件后,on-success回调的到了保存的URL,将这个URL拼接得到imageURL,通过v-if,向数据库发送请求得到图片的回显。