关于Java和JavaScript下载文件的小tip

简介

最近遇到一个下载文件的需求,因为文件不在本系统,可以直接请求远程的加载接口,不想通过本系统的后端再转一次。

于是就想通过前端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);
}

上面这种方式:

  1. 优点是简单
  2. 缺点是错误不友好,出错了,没有提示信息

如果希望错误信息友好一点,可以通过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);
    }
}

你可能感兴趣的:(java,javascript,js文件下载,如何优雅处理文件下载前后端,Java文件下载接口)