简介
最近遇到一个下载文件的需求,因为文件不在本系统,可以直接请求远程的加载接口,不想通过本系统的后端再转一次。
于是就想通过前端JavaScript直接下载。
下面介绍2种方式。
通过模拟form表单提交
function downloadRemoteFile(url,materialId) {var body = document.getElementsByTagName('body')[0];var form = document.createElement('form');form.method = 'POST';form.action = url;var param = document.createElement('input');param.type = "hidden";param.name = "materialId";param.value = materialId;form.appendChild(param);body.appendChild(form);form.submit();body.removeChild(form);
}
上面这种方式:
- 优点是简单
- 缺点是错误不友好,出错了,没有提示信息
如果希望错误信息友好一点,可以通过XMLHttpRequest方式。
通过XMLHttpRequest方式
function downloadRemoteFileXMLHttpRequest(url,materialId) {console.log("${downloadUrl}" + " " + materialId);var xhr = new XMLHttpRequest();xhr.open("POST", url, true);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")xhr.onload = function () {if (xhr.status === 200) {if (xhr.response == null || xhr.response == "" || xhr.response == undefined) {alert("下载文件出错,请检查文件是否存在:" + materialId);return;}// 获取判断Content-Type// var contentType = xhr.getResponseHeader("Content-Type");var blob = new Blob([xhr.response], { type: "application/octet-stream" });var fileName = getFileNameFromResponseHeader(xhr);var link = document.createElement("a");link.href = window.URL.createObjectURL(blob);link.download = fileName;link.click();link.remove();window.URL.revokeObjectURL(link.href);} else {alert("下载响应异常");console.log(xhr);}};xhr.send("materialId=" + materialId);
}function getFileNameFromResponseHeader(xhr) {var contentDisposition = xhr.getResponseHeader("content-disposition")var matchResult = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);if (matchResult != null && matchResult[1]) {return decodeURIComponent(matchResult[1].replace(/['"]/g, ""));}return "default-name";
}
通过XMLHttpRequest方式就灵活很多,虽然还是模拟了a链接,但是能先处理响应。
例如,下载后端可以能有一些预检查,如果预检查都没有通过,那么可能返回的就不是Blob文件,而是一个json。
通过XMLHttpRequest方式就可以通过判断ContentType内容,来获取文件流、或是json的内容,从而把错误信息比较友好的展示给用户。
对于后端下载接口,可能更好的方式是把信息写到header中,这样对于前端来说就能更好统一处理逻辑。
response.addHeader("success", "false");
response.addHeader("message", URLEncoder.encode("该数据您无权下载", StandardCharsets.UTF_8));
后端不同处理方式(仅仅演示,实际操作最后统一逻辑):
@RequestMapping("/download")
public void download(HttpServletResponse response, @RequestParam("fid") Long fid) throws IOException {if (fid < 0) {response.setContentType("application/json");PrintWriter writer = response.getWriter();Result<Void> result = ResultHelper.getFailResult("文件不存在");writer.write(JSON.toJSONString(result));return;}if (fid < 100) {response.addHeader("success", "false");String message = "该数据您无权下载";response.addHeader("message", URLEncoder.encode(message, StandardCharsets.UTF_8));return;}try (FileInputStream fis = new FileInputStream("F:\\tmp\\流动性季报统计表.xlsx");OutputStream os = response.getOutputStream()) {String fileName = URLEncoder.encode("流动性季报统计表.xlsx",StandardCharsets.UTF_8);response.reset();response.setContentType("application/octet-stream");response.setHeader("Content-Disposition", "attachment;filename=" + fileName);response.addHeader("success", "true");IOUtils.copy(fis, os);}
}