准备工作:导入maven jar包
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>axios</artifactId>
<version>1.5.1</version>
</dependency>
第一步:编写前端页面
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>下载中心</title>
<style>
.txt {
color: green;
}
</style>
</head>
<body>
<div id="app">
<h1>下载中心</h1>
<ol>
<li><span class="txt">微信小程序项目</span> <a href="javascript:void(0)" onclick="sendDownloadAsync('微信小程序项目.zip','d5cbcd2a-7e08-11ee-bf9c-54e1ad104b87')">下载</a></li>
<li><span class="txt">Java工程师简历模板</span> <a href="javascript:;" onclick="sendDownloadAsync('Java工程师简历模板.docx','cbae974f-7e08-11ee-bf9c-54e1ad104b87')">下载</a></li>
</ol>
</div>
<script src="/webjars/axios/1.5.1/dist/axios.js"></script>
<script>
// 功能:下载文件
function fileDownload(res) {
//headers:"attachment;filename=%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E9%A1%B9%E7%9B%AE.zip"
//表示从响应头中获取文件名
const fileName = decodeURI(res.headers['content-disposition']).replace(/\w+;filename=(.*)/, '$1')
console.log("解析的文件名称是",fileName)
//const blob = new Blob([data], {type: headers['content-type']})
const blob = new Blob([res.data], { type: "application/octet-stream"});
const fileUrl = window.URL.createObjectURL(blob)
let link = document.createElement('a') //创建一个a标签元素
link.href = fileUrl //设置a标签的href属性
link.download = decodeURI(fileName) //设置下载的文件名
link.style.display = 'none' //设置为不显示
document.body.appendChild(link) //将a标签元素加入到页面中
link.click() //自动触发单击事件
document.body.removeChild(link);// 下载完成移除元素
window.URL.revokeObjectURL(fileUrl); // 释放掉blob对象
}
// 功能:发起异步文件下载请求
function sendDownloadAsync(fileName,apiKey){
axios({
method: 'POST',
url: '/api/download/file',
data: {
fileName,
apiKey
},
headers: {
//"Authorization": localStorage.getItem("token")
"Content-Type": "application/x-www-form-urlencoded"
},
responseType: 'blob'
}).then(res => {
const data = res.data;
const fileReader = new FileReader();
// 进行对象装换,避免将后端接口返回的错误信息也当成文件下载
fileReader.onload = (event) => {
try {
const data = JSON.parse(event.target.result);
if (data.code !== 0) {
// 解析成对象失败,说明是普通对象数据
console.log(`下载提示: ${data.msg}`);
alert(`下载提示: ${data.msg}`)
}
} catch (err) {
// 解析成对象失败,说明是正常的文件流,可进行下载
// 正常下载文件
fileDownload(res)
}
};
fileReader.readAsText(data);
})
}
</script>
</body>
</html>
第二步:编写下载控制器
@RestController
@RequestMapping("/api/download")
public class DownloadController {
@Autowired
private UploadProperties uploadProperties;
// Redis
private static final List<String> APIKEY_LIST = new ArrayList<>(
Arrays.asList(
"d5cbcd2a-7e08-11ee-bf9c-54e1ad104b87",
"cbae974f-7e08-11ee-bf9c-54e1ad104b87"
)
);
/**
* 下载文件
*
* @param fileName 下载文件名
* @param apiKey apiKey
*/
@PostMapping("/file")
public void downloadFile(@RequestParam("fileName") String fileName,
@RequestParam("apiKey") String apiKey,
HttpServletResponse response) throws UnsupportedEncodingException, FileNotFoundException, BizException {
final String savePath = uploadProperties.getSavePath();
if (StringUtils.isBlank(fileName)) {
throw new BizException(400, "下载文件不能为空!");
}
if (StringUtils.isBlank(apiKey)) {
throw new BizException(400, "apiKey不能为空!");
}
if (!APIKEY_LIST.contains(apiKey)) {
throw new BizException(400, "apiKey无效,禁止下载文件!");
}
File downloadFile = new File(savePath, fileName);
if (!downloadFile.exists()) {
throw new BizException(400, "未找到需要下载的文件!");
}
File path = new File(ResourceUtils.getURL("classpath:").getPath());
System.out.println("path?>?>>>>>>>>>>>>>>>>>>>>>>>" + path);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
response.setContentType("application/octet-stream");
response.addHeader("Content-Length", downloadFile.length() + "");
// 禁止浏览器缓存
/*
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
其中,Cache-Control用于HTTP1.1(包括1.1)以上;Pragma用于HTTP1.0;Expires用于代理服务器缓存。
*/
response.addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.addHeader("Pragma", "no-cache");
response.addHeader("Expires", "0");
byte[] buffer = new byte[8 * 1024]; // 8kb
int readBytes = 0;
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(downloadFile));
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
) {
while ((readBytes = bis.read(buffer)) != -1) {
bos.write(buffer, 0, readBytes);
bos.flush();
}
response.flushBuffer();
APIKEY_LIST.remove(apiKey);
} catch (Exception ex) {
throw new BizException(500, "文件下载失败");
}
}
}
第三步:统一JSON结果
@ApiModel("统一响应结果")
public class CommonResult<T> {
@ApiModelProperty("响应状态码")
private int code;
@ApiModelProperty("响应提示信息")
private String msg;
@ApiModelProperty("响应的数据")
private T data;
public CommonResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public CommonResult(ResultCode resultCode) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
}
public CommonResult(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> CommonResult<T> success(T data) {
return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data);
}
public static <T> CommonResult<T> success(String msg) {
return new CommonResult<T>(0,msg);
}
public static <T> CommonResult<T> fail(ResultCode resultCode) {
return new CommonResult<T>(resultCode.getCode(), resultCode.getMsg());
}
public static <T> CommonResult<T> fail(int code, String msg) {
return new CommonResult<T>(code,msg);
}
public static <T> CommonResult<T> error() {
return new CommonResult<T>(ResultCode.SYSTEM_ERROR);
}
public static <T> CommonResult<T> error(int code,String msg) {
return new CommonResult<T>(code,msg);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
第三步:统一JSON结果 : 编写枚举类 ,进行code 和 msg 的返回
public enum ResultCode {
SUCCESS(0, "操作成功"),
FAILURE(1,"操作失败"),
PARAM_VALID_ERROR(400, "参数检验错误"),
UNAUTHORIZED(401, "未认证"),
FORBIDDEN(403, "无权限"),
SYSTEM_ERROR(500, "系统繁忙,请稍后再试!")
;
private int code;
private String msg;
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
第四步:全局异常处理器
public class BizException extends Exception{
private int code;
private String message;
public BizException(ResultCode resultCode) {
super(resultCode.getMsg());
this.code = resultCode.getCode();
this.message = resultCode.getMsg();
}
public BizException(int code,String message) {
super(message);
this.code = code;
this.message = message;
}
public BizException(int code,String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
/**
* 全局异常处理器
*/
@RestControllerAdvice
//@ControllerAdvice
//@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理缺少请求参数异常
* @param ex HttpMessageNotReadableException
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public CommonResult handleMissingException(
MissingServletRequestParameterException ex) {
log.error("缺少请求参数,{}", ex.getMessage());
return new CommonResult(400, "缺少必要的请求参数");
}
/**
* 处理缺少请求参数异常
* @param ex HttpMessageNotReadableException
* @return
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public CommonResult handleUploadSizeException(
MaxUploadSizeExceededException ex) {
log.error("文件上传最大允许大小超过{}字节。,", ex.getMaxUploadSize());
return new CommonResult(400, "上传文件大小超过"+ex.getMaxUploadSize()+"B");
}
/**
* 处理请求参数校验异常
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public CommonResult handleBindException(MethodArgumentNotValidException ex){
log.error("请求参数校验出现问题:{},异常类型:{}", ex.getMessage(), ex.getClass()); // for debug
String message = ex.getBindingResult().getFieldError().getDefaultMessage();
return CommonResult.error(400,message);
}
/**
* 处理自定义的业务异常
*/
@ExceptionHandler(value = BizException.class)
public CommonResult handleBizException(BizException ex){
log.error("业务操作问题:{},异常类型:{}", ex.getMessage(), ex.getClass()); // for debug
return CommonResult.error(ex.getCode(),ex.getMessage());
}
/**
* 处理系统异常
*/
@ExceptionHandler(value = Exception.class)
public CommonResult handleException(Exception ex){
log.error("系统出现问题:{},异常类型:{}", ex.getMessage(), ex.getClass()); // for debug
return CommonResult.error();
}
}