后台部分
①代码实现
package main.com.jt.firstsb.controller;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import main.com.jt.firstsb.config.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.stream.Stream;
@RestController
@RequestMapping("/files")
public class FileController {
@Value("${server.port}")
private String port;
private static final String ip="http://localhost";
/*
* 上传接口
* */
@PostMapping("/upload")
public Result<?> upload(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename(); //获取源文件的名称
//定义文件的唯一标识(前缀)
String flag = IdUtil.fastSimpleUUID();
String rootFilePath = System.getProperty("user.dir") + "/first-sb/src/main/resources/files/"+flag+"_"+originalFilename; //获取上传的路径
FileUtil.writeBytes(file.getBytes(),rootFilePath); //把文件写入到上传的路径
return Result.success(ip+":"+port+"/files/"+flag); //返回结果 url
}
/*
* 下载接口
* */
@GetMapping("/{flag}")
public void getFiles(HttpServletResponse response, @PathVariable String flag){
OutputStream os;
String basePath = System.getProperty("user.dir") + "/first-sb/src/main/resources/files/"; //定义文件下载的根路径
List<String> fileNames = FileUtil.listFileNames(basePath); //获取所有的文件名称
String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse("");
try{
if(StrUtil.isNotEmpty(fileName)){
response.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
response.setContentType("application/octet-stream");
byte[] bytes=FileUtil.readBytes(basePath+fileName); //通过文件的路径读取文件字节流
os=response.getOutputStream(); //通过输出流返回文件
os.write(bytes);
os.flush();
os.close();
}
} catch (Exception e){
System.out.println("文件下载失败");
}
}
}
②代码细节剖析:
@Value
该注解的作用是将我们配置文件的属性读出来,有
@Value(“${}”)
和
@Value(“#{}”)
两种方式。
-
@Value(“#{}”) 表示SpEl
表达式
通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量 -
用 @Value(“${xxxx}”)注解从配置文件读取值的用法
==>@Value(“${server.port}”) 可以读出配置文件中服务器的端口号
上传接口
1.接收文件需要使用MultipartFile类
2.文件的getOriginalName方法可以获取文件本身的名字
String originalFilename = file.getOriginalFilename(); //获取源文件的名称
3.IdUtil工具类由插件Hutool提供,可以快速获取uuid==>防止上传的文件名重复
String flag = IdUtil.fastSimpleUUID();
4.System.getProperty(“user.dir”) 获取的是启动项目的容器位置,用IDEA是项目的根目录,部署在tomcat上是tomcat的启动路径,即tomcat/bin的位置
String rootFilePath = System.getProperty("user.dir") + "/first-sb/src/main/resources/files/"+flag+"_"+originalFilename; //获取上传的路径
5.FileUtil由插件Hutool提供支持,writeBytes可以把文件写入到上传的路径
FileUtil.writeBytes(file.getBytes(),rootFilePath); //把文件写入到上传的路径
6.返回上传成功后文件所在的url,只要用户点击url,配合下载接口就可以下载文件
return Result.success(ip+":"+port+"/files/"+flag); //返回结果 url
下载接口
1. 定义文件下载的根路径
String basePath = System.getProperty("user.dir") + "/first-sb/src/main/resources/files/"; //定义文件上传的根路径
2.根据根路径找到所有的文件名
List<String> fileNames = FileUtil.listFileNames(basePath); //获取所有的文件名称
3.找到包含flag的文件名
因为flag是唯一标识,可以通过flag来确定唯一一个文件
findAny():findAny() 方法是获取流中任意一个,存在随机性,其实里面也是获取元素中的第一个。
orElse():如果不存在,则返回为空,可以指定默认值
String fileName = fileNames.stream().filter(name->name.contains(flag)).findAny().orElse("");
4.利用Hutool提供的工具类StrUtil的工具方法isNotEmpty()判空
if(StrUtil.isNotEmpty(fileName)){
5.添加响应头
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
attachment为以附件方式下载
disposition-parm为默认保存时的文件名
URLEncoder:对URL进行编码,用一种规则替换掉特殊字符
response.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
6.application/octet-stream:遇到content-type为application/octet-stream的文件时,浏览器会直接把它下载下来。
这个类型一般会配合另一个响应头Content-Disposition,该响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者网页的一部分),还是以附件的形式下载并保存到本地。
response.setContentType("application/octet-stream");
7.FileUtil为Hutool提供的工具类,使用工具方法readBytes,通过文件的路径读取文件字节流
byte[] bytes=FileUtil.readBytes(basePath+fileName); //通过文件的路径读取文件字节流
8.通过输出流返回文件
os=response.getOutputStream(); //通过输出流返回文件 os.write(bytes); os.flush(); os.close();
③跨域处理:
@Configuration
public class CorsConfig {
// 当前跨域请求最大有效时长,默认为1天
private static final long MAX_AGE=24*60*60;
private CorsConfiguration buildConfig(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); //设置访问源地址
corsConfiguration.addAllowedHeader("*"); //设置访问源请求头
corsConfiguration.addAllowedMethod("*"); //设置访问源请求方法
corsConfiguration.setMaxAge(MAX_AGE);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",buildConfig()); //对接口配置跨域设置
return new CorsFilter(source);
}
}
前台部分
只需要使用elementUI提供的Upload组件即可
<el-form-item label="封面">
<el-upload
action="http://localhost:9090/files/upload"
:on-success="filesUploadSuccess"
v-model="form.cover"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
</el-form-item>
对应的filesUploadSuccess方法:
filesUploadSuccess(response){
this.form.cover=response.data
},
实现效果:
展示封面:
注意对于文件类型,不能使用prop属性,而应该使用模板
加上属性preview-src-list,有大图预览效果
<el-table-column
label="封面"
>
<template #default="scope">
<el-image
style="width: 100px; height: 100px"
:src="scope.row.cover"
:preview-src-list="[scope.row.cover]"></el-image>
</template>
</el-table-column>
实现效果:
最后:可以通过下列语句清除已经上传成功的文件列表
this.$refs['upload'].clearFiles()