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

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

1.jpeg

1.背景描述

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

image.png

image.png

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",
        ...

image.png

如图1

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

答案:
image.png

最终方案:不采用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);
            }
        }
    }
目录
相关文章
|
6天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
26天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
36 6
|
26天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
32 2
|
17天前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
7天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
21 1
|
12天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
40 4
|
18天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
17天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
17天前
|
Java 测试技术 API
Java零基础-接口详解
【10月更文挑战第19天】Java零基础教学篇,手把手实践教学!
17 1
|
22天前
|
Web App开发 Java
使用java操作浏览器的工具selenium-java和webdriver下载地址
【10月更文挑战第12天】Selenium-java依赖包用于自动化Web测试,版本为3.141.59。ChromeDriver和EdgeDriver分别用于控制Chrome和Edge浏览器,需确保版本与浏览器匹配。示例代码展示了如何使用Selenium-java模拟登录CSDN,包括设置驱动路径、添加Cookies和获取页面源码。