胶州建设局网站,网页设计实验,百度网页版电脑版,做网站责任1、问题描述 我想通过异步的方式实现下载文化#xff0c;请求为post请求。一开始我打算用ajax。
$.ajax({type:post,contentType:application/json,url:http://xxx/downloadExcel,data:{data:JSON.stringify(%oJsonResponse.JSONoutput()%)},}).success(function(dat…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 url1URL.createObjectURL(blob)const a document.createElement(a);a.href url1;a.download 表格.xlsx;a.click(); URL.revokeObjectURL(url1);}); 不过ajax的返回类型不支持二进制文件流(binary)因此ajax的异步方式无法接到后端接口返回的文件流就无法下载文件。 jQuery.ajax() | jQuery API Documentation 2、解决方法 改用dom原生的XMLHttpRequest。 XMLHttpRequest的返回类型二进制数据blob可以接到文件流。
XMLHttpRequest.responseType - Web API 接口参考 | MDN (mozilla.org) 3、代码示例
3.1、前端代码 downloadExcel.html
!DOCTYPE html
htmlheadmeta charsetutf-8title/title/headbodyscript typetext/javascript src./js/jquery.min.js/scriptscript typetext/javascript$(document).ready(function() {$(#btnDownload).click(function(){var param{name:zhangsan,age:20,sex:男};let xhrnew 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();}}}});});/scriptbutton classbtn idbtnDownload namebtnDownload下载文件/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 {Autowiredprivate MyExcelUtils myExcelUtils;PostMapping(/downloadExcel)CrossOrigin //跨域public String downloadExcel(HttpServletResponse response, RequestBody MapString, 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(MapString, Object map) throws IOException {Resource resource new ClassPathResource(/);String path resource.getFile().getPath();String filePath path excel.xlsx;ListPeopleBO 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;}
} 文件是能成功下载了。 但后端报了一个错误 产生的原因是因为返回了多次response了。因为一个接口只能有一个return即有一个response响应给到调用方。但downloadExcel接口出现了两个response但一个接口只能有一个response响应因此另一个response就失效了就会出现sendError()的报错。 解决方法有三种
1、输出流不要关闭不推荐 因为流一旦关闭就意味着基本上就结束了对客户端的响应了。下面的return success就没法返回给调用方了。又因为是RestController需要一个可以封装成json的返回对象显然“流”是不能封装成json的RestController需要下面的return success但你提前把“流”关闭了return success不会响应RestController封装不到json对象就会报错了。
2、controller接口或者util方法改为void推荐 不要return myExcelUtils.downloadExcel(myExcelUtils.composeFile(data), response);
把return去掉直接myExcelUtils.downloadExcel(myExcelUtils.composeFile(data), response);
3、避免使用RestController改用Controller RestControllerControllerResponseBody而ResponseBody会把返回值封装成json的形式返回。如果不加ResponseBody则底层会把返回值封装成一个ModelAndView对像。显然文件流并不能封装成json但由于通常在输出文件流后会把这个流关闭因此下面那个可以封装成json对象的return返回值就不能返回了。因此就会报错了。当然除非你一直让outputStream保持打开使response响应不关闭。但不推荐这么做文件流还是要用完及时关闭的。因为OutputStream也属于资源处理完了以后务必要close()关闭并释放此流有关的所有系统资源不然会大量占用系统内存资源大量不释放资源会导致内存溢出。 4、总结 我们在jquery中常用的ajax其实就是对XMLHttpRequest进行了封装。ajax的底层就是XMLHttpRequest。jquery的出现主要就是为了更快捷的操作DOM以及解决一些浏览器兼容性问题。jquery$.ajax通过对XHRXMLHttpRequest简称XHR封装做了兼容性的处理简化了使用增加了对JSONP的支持。 JSONP类型可以支持跨域因为jsonp不受同源策略的影响。所谓同源策略”源“指的是协议名http/https、域名/Ip地址、端口号。不同源的客户端/服务端在没有对方授权的情况下是不允许发送/接收对方的数据资源的会产生“跨域”情况。 JSONP用法举例 前端
script typetext/javascript src./js/jquery.min.js/scriptscript typetext/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(successdata) });});});function showData(data){console.info(回调showDatadata);}/scriptbutton classbtn idbtnJSONP namebtnJSONPtestJSONP/button 后端 GetMapping(/testJsonP)public void testJsonP(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setCharacterEncoding(UTF-8);response.setContentType(text/html;charsetUTF-8);//前端传过来的回调函数名称String callback request.getParameter(jsoncallback);//用回调函数名称包裹返回数据这样返回数据就作为回调函数的参数传回去了String result callback (Hello World);response.getWriter().write(result);} 可以看到前后端不同源但不用专门设置什么就可以实现通信。这就是JSONP的跨域。
如果不用jsonp的话用XMLHttpRequest或者ajax的话则要设置一下 后端接口加上CrossOrigin注解设置response “Access-Control-Allow-Origin”请求头为“*”
response.addHeader(Access-Control-Allow-Origin, *);
不然就会出现跨域报错 除了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;charsetUTF-8 not supported_北宫清云的博客-CSDN博客 jQuery.ajax() | jQuery API Documentationjava 关闭输出流_Java OutputStream.close()关闭并释放输出流资源_卖糕郎的博客-CSDN博客
varletconst三者的特点和区别_前端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