写点什么

java 中 post 请求调用下载文件接口浏览器未弹窗而是返回一堆 json,为啥

作者:刘大猫
  • 2025-08-21
    黑龙江
  • 本文字数:4658 字

    阅读完需:约 15 分钟

@[toc]

1.背景描述

客户端调接口需要返回另存为弹窗,下载文件,但是遇到的问题是接口调用成功且不报错,浏览器 F12 查看居然返回一堆 json,而没有另存为弹窗;正确的效果应该是:接口调用成功且浏览器 F12 不返回任何 json,而是弹窗另存为窗口,直接保存文件即可。



2.项目代码

代码说明:具体的引入或者工具类啥的就不复制粘贴了,就是你可以理解为给你个硬盘地址,然后封装成 File,以流输出,或者以 poi 依赖包的 WorkBook 输出流都可以。


前端 js 代码


//批量管理-下载批量导入数据function downloadBatchImportDataTaskActionColumn(taskId) {    var param = {        taskId: taskId    }    $.ajax({        async: true,        url: prefix + "/downloadBatchImportData",        type: 'post',        data: JSON.stringify(param),        dataType: 'json',        contentType: "application/json;charset=UTF-8",        beforeSend:function(){            window.parent.showLoading();        },        success: function (res) {            window.parent.completeLoading();            console.log(res);
}, error:function(){ window.parent.completeLoading(); } });}
复制代码


后端代码


/**     * 下载批量导入数据     * @param req req     * @param response response     */    @RequestMapping(value = "/downloadBatchImportData")    public void downloadBatchImportData(HttpServletRequest req, @RequestBody QueryTaskRes queryTaskRes, HttpServletResponse response) {//        String taskId = req.getParameter("taskId");//        logger.info("-downloadBatchImportData-taskId:{}", taskId);        OutputStream os = null;        InputStream io = null;        String tempFilePath = TEMP_FILE_PATH;        String fileName = "";
try { ImpExpTaskDetail impExpTaskDetail = isvcBatchTaskServiceMicro.selectTaskDetailByTaskId(queryTaskRes.getTaskId()); String filedownLink = impExpTaskDetail.getLink(); if (org.springframework.util.StringUtils.isEmpty(filedownLink)) { fileName = MessageUtils.message("batch.download") + ".xlsx"; } else { fileName = StringUtils.subscribeNameString(filedownLink); } logger.info("-tempFilePath:{},fileName:{}", tempFilePath, fileName);
File file = ResourceUtils.getFile(String.join(File.separator, tempFilePath, fileName)); if (!file.exists()) { String begin = DateUtil.now(); DateTime beginTime = DateUtil.parse(begin); long download = HttpUtil.downloadFile(filedownLink, FileUtil.file(tempFilePath, fileName), new StreamProgress() { @Override public void start() { logger.info("开始下载,时间为:" + begin); }
@Override public void progress(long progressSize) { logger.info("已下载:{}", FileUtil.readableFileSize(progressSize)); }
@Override public void finish() { String end = DateUtil.now(); DateTime endTime = DateUtil.parse(end); long between = DateUtil.between(beginTime, endTime, DateUnit.MS); logger.info("下载完成,用时:" + DateUtil.formatBetween(between, BetweenFormatter.Level.SECOND)); } }); }
io = new FileInputStream(file); Workbook wb = new XSSFWorkbook(io); response.setContentType("application/octet-stream;charset=UTF-8"); response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8)); wb.write(response.getOutputStream()); } catch (IOException e) { logger.error("-downloadBatchImportData error:{}", e.getMessage()); } finally { if (os != null) { try { os.close(); } catch (IOException e) { logger.error("-OutputStream error:{}", e.getMessage()); } } if (io != null) { try { io.close(); } catch (IOException e) { logger.error("-InputStream error:{}", e.getMessage()); } } if (Optional.ofNullable(tempFilePath).isPresent()) { // 强制删除临时文件 boolean isDelete = com.hytalk.util.FileUtil.delFile(new File(tempFilePath)); logger.info("-downloadBatchImportData 强制删除临时文件 , filePath: {} , isDelete : {} ", tempFilePath, isDelete); } } }
复制代码

3.导致错误原因分析

最终的错误原因就是: 因为使用了 ajax 发请求,请看下方代码,这里面的 dataType 和 contentType 用来设置传参类型及返回类型,只要设置这两个返回的就是 json 字符串,而不会以文件流输出。但是如果不写这两 dataType+contentType 值,那么 contentType 的默认值为 application/x-www-form-urlencoded,最终效果也不行而且会报错。报错如图 1。


$.ajax({        async: true,        url: prefix + "/downloadBatchImportData",        type: 'post',        data: JSON.stringify(param),        dataType: 'json',        contentType: "application/json;charset=UTF-8",        ...
复制代码



<center><font color='red'>如图 1</font></center>


问题:为啥会报如图 1 中的错误?


答案:



最终方案:不采用 ajax 发送请求,而是采用最普遍的 form 表单的方式提交就可以实现效果。


前端 js 代码


//批量管理-下载批量导入数据function downloadBatchImportDataTaskActionColumn(taskId) {    var url = prefix + "/downloadBatchImportData";    var form = $("<form></form>").attr("action", url).attr("method", "post");    form.append($("<input></input>").attr("type", "hidden").attr("name", "taskId").attr("value", taskId));    form.appendTo('body').submit().remove();}
复制代码


后端代码


/**     * 下载批量导入数据     * @param req req     * @param response response     */    @RequestMapping(value = "/downloadBatchImportData")    public void downloadBatchImportData(HttpServletRequest req, HttpServletResponse response) {        String taskId = req.getParameter("taskId");        logger.info("-downloadBatchImportData-taskId:{}", taskId);        OutputStream os = null;        InputStream io = null;        String tempFilePath = TEMP_FILE_PATH;        String fileName = "";
try { ImpExpTaskDetail impExpTaskDetail = isvcBatchTaskServiceMicro.selectTaskDetailByTaskId(taskId); String filedownLink = impExpTaskDetail.getLink(); if (org.springframework.util.StringUtils.isEmpty(filedownLink)) { fileName = MessageUtils.message("batch.download") + ".xlsx"; } else { fileName = StringUtils.subscribeNameString(filedownLink); } logger.info("-tempFilePath:{},fileName:{}", tempFilePath, fileName);
File file = ResourceUtils.getFile(String.join(File.separator, tempFilePath, fileName)); if (!file.exists()) { String begin = DateUtil.now(); DateTime beginTime = DateUtil.parse(begin); long download = HttpUtil.downloadFile(filedownLink, FileUtil.file(tempFilePath, fileName), new StreamProgress() { @Override public void start() { logger.info("开始下载,时间为:" + begin); }
@Override public void progress(long progressSize) { logger.info("已下载:{}", FileUtil.readableFileSize(progressSize)); }
@Override public void finish() { String end = DateUtil.now(); DateTime endTime = DateUtil.parse(end); long between = DateUtil.between(beginTime, endTime, DateUnit.MS); logger.info("下载完成,用时:" + DateUtil.formatBetween(between, BetweenFormatter.Level.SECOND)); } }); }
io = new FileInputStream(file); Workbook wb = new XSSFWorkbook(io); response.setContentType("application/octet-stream;charset=UTF-8"); response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8)); wb.write(response.getOutputStream()); } catch (IOException e) { logger.error("-downloadBatchImportData error:{}", e.getMessage()); } finally { if (os != null) { try { os.close(); } catch (IOException e) { logger.error("-OutputStream error:{}", e.getMessage()); } } if (io != null) { try { io.close(); } catch (IOException e) { logger.error("-InputStream error:{}", e.getMessage()); } } if (Optional.ofNullable(tempFilePath).isPresent()) { // 强制删除临时文件 boolean isDelete = com.hytalk.util.FileUtil.delFile(new File(tempFilePath)); logger.info("-downloadBatchImportData 强制删除临时文件 , filePath: {} , isDelete : {} ", tempFilePath, isDelete); } } }
复制代码


用户头像

刘大猫

关注

还未添加个人签名 2022-08-23 加入

还未添加个人简介

评论

发布
暂无评论
java中post请求调用下载文件接口浏览器未弹窗而是返回一堆json,为啥_深度学习_刘大猫_InfoQ写作社区