最近遇到一个下载文件的需求,因为文件不在本系统,可以直接请求远程的加载接口,不想通过本系统的后端再转一次。
于是就想通过前端JavaScript直接下载。
下面介绍2种方式。
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方式。
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);
}
}