使用XMLHttpRequest实现文件异步下载

这篇具有很好参考价值的文章主要介绍了使用XMLHttpRequest实现文件异步下载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、问题描述

        我想通过异步的方式实现下载文化,请求为post请求。一开始我打算用ajax。

$.ajax({
        type:'post',
        contentType:'application/json',
        url:'http://xxx/downloadExcel',
        data:{data:JSON.stringify(<%=oJsonResponse.JSONoutput()%>)},
        }).success(function(data){
                    const blob = new Blob([data], {type: 'application/vnd.openxmlformats- 
                    officedocument.spreadsheetml.sheet'});
                    const url1=URL.createObjectURL(blob)
                    const a = document.createElement('a');
                    a.href = url1;
                    a.download = '表格.xlsx';
                    a.click(); 
                    URL.revokeObjectURL(url1);

        });

        不过ajax的返回类型不支持二进制文件流(binary)!因此ajax的异步方式无法接到后端接口返回的文件流,就无法下载文件。

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

jQuery.ajax() | jQuery API Documentation

2、解决方法

        改用dom原生的XMLHttpRequest。

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

         XMLHttpRequest的返回类型二进制数据blob,可以接到文件流。

XMLHttpRequest.responseType - Web API 接口参考 | MDN (mozilla.org)

3、代码示例

3.1、前端代码

         downloadExcel.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<script type="text/javascript" src="./js/jquery.min.js"></script>
		
		<script type="text/javascript">
			$(document).ready(function() {
				$("#btnDownload").click(function(){
					var param={name:'zhangsan',age:'20',sex:'男'};
					let xhr=new XMLHttpRequest(); 
					xhr.responseType = "blob";
					xhr.open('POST', 'http://localhost:6001/excel/downloadExcel');
					xhr.setRequestHeader('Content-Type', 'application/json');
					xhr.send(JSON.stringify(param));
					xhr.onreadystatechange = function() {
						console.log(xhr.response)
						if (xhr.status === 200) {
							var blob = xhr.response;
							if(blob){
								var downloadLink = document.createElement('a');
								downloadLink.href = URL.createObjectURL(blob);
								downloadLink.download = 'excel.xlsx'; // 设置下载的文件名
								downloadLink.click();
							}
							
						}
					}
				});
			});
		</script>
		
		<button class="btn" id="btnDownload" name="btnDownload">下载文件</button>
	</body>
</html>

 3.2、后端代码

ExcelController.java
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author Wulc
 * @date 2023/7/20 16:02
 * @description
 */
@RestController
@RequestMapping("/excel")
public class ExcelController {
    @Autowired
    private MyExcelUtils myExcelUtils;

    @PostMapping("/downloadExcel")
    @CrossOrigin //跨域
    public String downloadExcel(HttpServletResponse response, @RequestBody Map<String, Object> data) throws IOException {
        return myExcelUtils.downloadExcel(myExcelUtils.composeFile(data), response);
    }
}
MyExcelUtils.java
package com.easyexcel.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.easyexcel.bo.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Wulc
 * @date 2023/7/25 17:07
 * @description
 */
@Component
public class MyExcelUtils {

    public File composeFile(Map<String, Object> map) throws IOException {
        Resource resource = new ClassPathResource("/");
        String path = resource.getFile().getPath();
        String filePath = path + "excel.xlsx";
        List<PeopleBO> peopleBOList = new ArrayList<>();
        peopleBOList.add(new PeopleBO(map.get("name").toString(), map.get("age").toString(), map.get("sex").toString()));
        EasyExcel.write(filePath, PeopleBO.class).sheet().useDefaultStyle(false).needHead(true).doWrite(peopleBOList);
        return new File(filePath);
    }

    public String downloadExcel(File file, HttpServletResponse response) {
        try {
            // 获取文件名
            String filename = file.getName();
            // 获取文件后缀名
            String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
            // 将文件写入输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStream fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            response.addHeader("Content-Length", "" + file.length());
            //跨域
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.setContentType("application/octet-stream");
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            outputStream.write(buffer);
            outputStream.flush();
            outputStream.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            file.delete();
        }
        return "success";
    }
}

文件是能成功下载了。 

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

 但后端报了一个错误

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

 产生的原因,是因为返回了多次response了。因为一个接口只能有一个return,即有一个response响应给到调用方。但downloadExcel接口出现了两个response,但一个接口只能有一个response响应,因此另一个response就失效了,就会出现sendError()的报错。

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

 解决方法有三种

1、输出流不要关闭(不推荐)

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

因为流一旦关闭,就意味着基本上就结束了对客户端的响应了。下面的return "success"就没法返回给调用方了。又因为是@RestController,需要一个可以封装成json的返回对象,显然“流”是不能封装成json的,@RestController需要下面的return "success",但你提前把“流”关闭了,return "success"不会响应,@RestController封装不到json对象,就会报错了。

2、controller接口或者util方法改为void(推荐)

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

 不要return myExcelUtils.downloadExcel(myExcelUtils.composeFile(data), response);

把return去掉,直接myExcelUtils.downloadExcel(myExcelUtils.composeFile(data), response);

3、避免使用@RestController,改用@Controller

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

 @RestController=@Controller+@ResponseBody,而@ResponseBody会把返回值封装成json的形式返回。如果不加@ResponseBody,则底层会把返回值封装成一个ModelAndView对像。显然文件流并不能封装成json,但由于通常在输出文件流后,会把这个流关闭,因此下面那个可以封装成json对象的return返回值就不能返回了。因此就会报错了。当然除非你一直让outputStream保持打开,使response响应不关闭。但不推荐这么做,文件流还是要用完及时关闭的。因为OutputStream也属于资源,处理完了以后务必要close()关闭并释放此流有关的所有系统资源,不然会大量占用系统内存资源,大量不释放资源会导致内存溢出。

4、总结

        我们在jquery中常用的ajax其实就是对XMLHttpRequest进行了封装。ajax的底层就是XMLHttpRequest。jquery的出现主要就是为了更快捷的操作DOM,以及解决一些浏览器兼容性问题。jquery$.ajax通过对XHR(XMLHttpRequest简称XHR)封装,做了兼容性的处理,简化了使用,增加了对JSONP的支持。

        JSONP类型可以支持跨域,因为jsonp不受同源策略的影响。所谓同源策略,”源“指的是:协议名(http/https)、域名/Ip地址、端口号。不同源的客户端/服务端,在没有对方授权的情况下是不允许发送/接收对方的数据资源的,会产生“跨域”情况。

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

         JSONP用法举例:

        前端

<script type="text/javascript" src="./js/jquery.min.js"></script>
		<script type="text/javascript">
            $(document).ready(function() {
                $("#btnJSONP").click(function(){
					var param={name:'zhangsan',age:'20',sex:'男'};
					$.ajax({ 
						 url: 'http://localhost:6001/excel/testJsonP', // 跨域URL 
						 type:'get',
						 dataType: 'jsonp',
						 jsonp:'jsoncallback',//自定义参数名称
						 jsonpCallback: 'showData', //指定回调函数名称
						 //timeout: 5000, 
					 }).success(function(data){
					 	console.log("success:"+data)	
					 });
				});
            });
			function showData(data){
				console.info("回调showData:"+data);
			}
				
		</script>

<button class="btn" id="btnJSONP" name="btnJSONP">testJSONP</button>

         后端

 @GetMapping("/testJsonP")
 public void testJsonP(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //前端传过来的回调函数名称
        String callback = request.getParameter("jsoncallback");
        //用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
        String result = callback + "('Hello World')";
        response.getWriter().write(result);
    }

使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

可以看到前后端不同源,但不用专门设置什么,就可以实现通信。这就是JSONP的跨域。

如果不用jsonp的话,用XMLHttpRequest或者ajax的话,则要设置一下

 后端接口加上@CrossOrigin注解,设置response “Access-Control-Allow-Origin”请求头为“*”

response.addHeader("Access-Control-Allow-Origin", "*");

不然就会出现跨域报错:使用XMLHttpRequest实现文件异步下载,工作,前端,XMLHttpRequest,ajax

 除了XHR和ajax,在前端框架中广泛Http数据通信工具:fetch、axios。fetch和XMLHttpRequest一样都是底层的原生js,只不过Fetch是基于promise设计的适用于前端框架。

而ajax和axios都是封装了XMLHttpRequest,一个适用于jquery,一个则广泛运用于各种主流前端框架:vue、react等等。

5、参考资料

ajax跨域请求错误-CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource_ajax cors错误_我是一朵蒲公英的博客-CSDN博客

 Ajax传JSON对象报错:JSON parse error: Unrecognized token ‘ids‘: was expecting (‘true‘, ‘false‘ or ‘null‘);_萌宅鹿同学的博客-CSDN博客

Can not construct instance of java.util.LinkedHashMap: no String-argument constructor/factory method_-droidcoffee-的博客-CSDN博客 【Java】解决POST表单提交报错 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported_北宫清云的博客-CSDN博客

 jQuery.ajax() | jQuery API Documentationjava 关闭输出流_Java OutputStream.close()关闭并释放输出流资源_卖糕郎的博客-CSDN博客

var,let,const三者的特点和区别_前端Vincent的博客-CSDN博客

已解决【Error】Cannot call sendError() after the response has been committed_hah杨大仙的博客-CSDN博客 springBoot文件下载出现 Cannot call sendError() after the response has been committed异常_木羊子羽的博客-CSDN博客

解决:java.lang.IllegalStateException: Cannot call sendError() after the response has been committed_java 转发 报cannot call sendredirect after the respon_郄子硕-langgeligelang的博客-CSDN博客 Controller和RestController的区别_controller与restcontroller区别_Linux资源站的博客-CSDN博客jsonp解决跨域问题_jsonp跨域_Ivymemphis的博客-CSDN博客

https://www.cnblogs.com/chiangchou/p/jsonp.html文章来源地址https://www.toymoban.com/news/detail-631235.html

到了这里,关于使用XMLHttpRequest实现文件异步下载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AJAX 使用 JavaScript 的 `XMLHttpRequest` 对象来向服务器发送异步请求

    AJAX 是一种使用异步 HTTP (Ajax) 请求获取和发送数据的技术。它使得网页能够进行异步更新,而不需要重新加载整个页面。通过使用 AJAX,可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容。 AJAX 使用 JavaScript 的 XMLHttpRequest 对象来向服务器发送异步请

    2024年01月16日
    浏览(44)
  • 【解决】axios 下载文件 Failed to read the 'responseText' property from 'XMLHttpRequest'

    主要解决以下两个问题 问题一:idm一些网站不允许请求同一文件两次 故障原因: IDM 在发神经 因为它检测到浏览器集成插件未安装,所以诱导你安装。实际上,装了插件问题也会出现。改参数都没用。 1.很可能是你点击网页的 下载链接 有问题(换个网页下载试试,就不提示

    2024年02月01日
    浏览(29)
  • 前端实现文件下载功能——文件流

    前端下载文件一般使用的是blob 核心的步骤是获取后端响应的文件流,用blob创建一个临时的URL,然后创建一个隐藏的a标签,实现下载需求。 那就先上代码 如果后端响应的数据是一个二进制数据,那我们就得这是响应类型是blob,否则浏览器会默认按照json解析 至于后端如何向

    2024年02月11日
    浏览(34)
  • 前端实现下载文件(包含压缩包下载)方式汇总

    默认最简单的下载方式是: window.open(后台接口API路径) ,但该方法弊端:因是新开窗口方式,前端展示上,每次会闪下。 此外,如果使用window.open(文件URL)方式: pdf、office文档、psd:直接下载。 图片、txt:新开窗口预览,不会下载;且txt预览,有时出现中文乱码问题。 一、

    2024年02月10日
    浏览(40)
  • 前端发送请求获取后端文件,并且前端实现下载文件功能

    最近遇到一个需求,就是前端发送post请求获取后端的excel文件,并且前端实现下载导出到本地的功能。 前端页面就长上面那样,一个批量导出功能,用户勾选之后,前端通过接口把id和其他的参数传给后端,接口调用方法这里需要注意的是,这里必须设置responseType: ‘blob’

    2024年02月09日
    浏览(31)
  • 前端实现下载文件的方法汇总

    对于前端来说,下载文件是一个特别常见的需求。但是前端要根据后端返回的内容,来选择下载文件的具体方法。通常情况下,后端要么给你 返回文件的网络地址 ,要么 返回文件流 。返回地址一般用在静态文件上,比如图片和音视频资源等;返回文件流一般用在动态文件上

    2024年02月09日
    浏览(32)
  • 前端实现下载文件的各种方式

    前端涉及到的文件下载还是很多应用场景的,那么前端文件下载有多少种方式呢?每种方式有什么优缺点呢?下面就来一一介绍。 通过 a 标签的 download 属性来实现文件下载,这种方式是最简单的,也是我们比较常用的方式,先来看示例代码: 就上面的这个示例,我们点击下

    2024年02月16日
    浏览(36)
  • 前端下载文件的几种方式使用Blob下载文件

    前端下载文件的几种方式 使用Blob下载文件 在前端下载文件是个很通用的需求,一般后端会提供下载的方式有两种: 1.直接返回文件的 网络地址 (一般用在静态文件上,比如图片以及各种音视频资源等) 2.返回 文件流 (一般用在动态文件上,比如根据前端选择,导出不同的

    2024年02月05日
    浏览(34)
  • 【Java 实现文件下载】vue前端+java后端实现文件下载详解(附源码)

    【 写在前面 】前端时间总结了一下有关java文件上传的功能,也给不少读者带来一些帮助,因此今天继续完善文件下载这套体系,希望能给屏幕前的您带来实质性的帮助,其实文件下载最怕的就是中文乱码的现象,当然这个我单独写了一篇文章解释,这里不做详谈。 涉及知识

    2024年02月09日
    浏览(32)
  • 前端下载文件(Blob)的几种方式使用Blob下载文件

    在前端下载文件是个很通用的需求,一般后端会提供下载的方式有两种: 1.直接返回文件的网络地址(一般用在静态文件上,比如图片以及各种音视频资源等) 2.返回文件流(一般用在动态文件上,比如根据前端选择,导出不同的统计结果 excel 等) 第一种方式比较简单,但

    2024年02月07日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包