在应用开发中,经常需要下载文件(如导出Excel),调用后台接口时,如果后台报错需要弹出错误信息,如果没有报错正常下载文件。本文主要介绍前台及后台(基于java)的处理方法,文中使用到的软件版本:Spring 4.3.9、Java 1.8.0_191、Jquery 1.12.4、Chrome 85.0.4183.121。

1、实现思路

方法一:通过原生Ajax响应头来区分是文本还是流,然后通过blob来处理返回数据

方法二:分成两个接口,先调用生成文件的接口,如果文件成功生成则再调用文件下载的接口;如果文件生成失败则弹出失败信息

2、方法一(Ajax响应头区分文本还是流)

2.1、后台


/**
* 根据sql导成csv;成功返回文件流,失败返回错误信息
* @param sql 导出sql
* @param col 字段信息,以逗号分隔
* @param title 文件头信息,以逗号分隔
* @param separator csv文件的分隔符
* @param baseServiceName
* @param request
* @param response
*/
@RequestMapping("exportForCsvStream")
@ResponseBody
public R<String> exportForCsvStream(String sql, String col, String title, String separator, String fileName, String baseServiceName, HttpServletRequest request, HttpServletResponse response) {
logger.info("sql={},title={},separator={},fileName={},baseServiceName={}", sql, title, separator, fileName, baseServiceName);
try {
if (StringUtils.isBlank(separator)) {
separator = ",";
}
if (StringUtils.isBlank(fileName)) {
fileName = "data.csv";
}
setResponse(fileName, request, response);
OutputStream os = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);
//调用service把文件写入到输出流,请自行实现
this.getBaseService(baseServiceName).exportForCsv(os, sql, col, title, separator);
os.close();

return null;
} catch(Exception e) {
e.printStackTrace();
response.reset();
response.setContentType("application/json");
response.setHeader("Content-Disposition", "");
return R.error(-1, "发生异常");
}
}

private void setResponse(String fileName, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
String urlFileName = "";
if (request.getHeader("User-Agent").toLowerCase().indexOf("firefox") > 0) {
urlFileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
} else {
urlFileName = java.net.URLEncoder.encode(fileName, "UTF-8");
}

response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + urlFileName + "\"");
response.setHeader("Connection", "close");
}


2.2、前台

2.2.1、拼接参数


function expForCsv() {
let sql = "";//请自行设置
let col = "";//请自行设置
let title = "";//请自行设置
let fileName = "";//请自行设置

$.messager.progress({title : '处理中...'});//easyui控件,可用其他控件
   let xhr = new XMLHttpRequest();
xhr.open("post", "${pageContext.request.contextPath}/common/export/exportForCsvStream.do", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function() {
     $.messager.progress('close');
if (xhr.getResponseHeader("Content-type") == 'application/octet-stream') {
let url = window.URL.createObjectURL(xhr.response);
let a = document.createElement("a");
a.href = url;
a.style.display = 'none'
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
a.remove();
} else {
let reader = new FileReader();
reader.addEventListener('loadend', function (e) {
let data = JSON.parse(e.target.result);
alert('提示', "系统出错,错误信息为:" + data.description + ",请将该信息提供给代维人员寻求帮助");
});
reader.readAsText(xhr.response);
}
};
xhr.send('sql=' + sql + '&col=' + col + '&title=' + title + '&separator=|&t=' + new Date().getTime());
}


2.2.2、使用FormData


function expForCsv(type) {
let sql = "";//请自行设置
let col = "";//请自行设置
let title = "";//请自行设置
let fileName = ";//请自行设置

$.messager.progress({title : '处理中...'});//easyui控件,可用其他控件
let xhr = new XMLHttpRequest();
xhr.open("post", "${pageContext.request.contextPath}/common/export/exportForCsvStream.do", true);
//xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function() {
$.messager.progress('close');
if (xhr.getResponseHeader("Content-type") == 'application/octet-stream') {
let url = window.URL.createObjectURL(xhr.response);
let a = document.createElement("a");
a.href = url;
a.style.display = 'none'
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
a.remove();
} else {
let reader = new FileReader();
reader.addEventListener('loadend', function (e) {
let data = JSON.parse(e.target.result);
alert('提示', "系统出错,错误信息为:" + data.description + ",请将该信息提供给代维人员寻求帮助");
});
reader.readAsText(xhr.response);
}
};
let data = new FormData();
data.append('sql', getCsql(type));
data.append('col', col);
data.append('title', title);
data.append('separator', '|');
data.append('t', new Date().getTime() + "");
xhr.send(data);
}


3、方法二(两个接口)

3.1、后台

3.1.1、生成文件接口


/**
* 根据sql导成csv;成功返回生成的文件名称,失败返回错误信息
* @param sql 导出sql
* @param col 字段信息,以逗号分隔
* @param title 文件头信息,以逗号分隔
* @param separator csv文件分隔符
* @param baseServiceName
* @return
*/
@RequestMapping("exportForCsv")
@ResponseBody
public CallResult<String> exportForCsv(String sql, String col, String title, String separator) {
logger.info("sql={}", sql);
logger.info("col={}", col);
logger.info("title={}", title);
logger.info("separator={}", separator);
logger.info("baseServiceName={}", baseServiceName);
OutputStream os = null;
CallResult<String> result = new CallResult<>();
try {
String fileName = "数据信息(" + DateUtil.getCurrentDateString("yyyyMMddHHmmsss") + ").xlsx";
os = new FileOutputStream(new File(WebconfigFactory.getTempFolder() + "数据信息(" + DateUtil.getCurrentDateString("yyyyMMddHHmmsss") + ").xlsx"));
     
//调用service生成文件,请自行实现
getService().exportForCsv(os, sql, col, title, separator);

result.setResult(fileName);
} catch(Exception e) {
result = new CallResult<>(-1, "发生异常");
e.printStackTrace();
} finally {
FileUtil.close(os);
}

return result;
}


3.1.2、下载文件接口


/**
* 下载方法
* 1.点击页面下载,需传参数:
* fileName:磁盘上文件的名称,必需
* filePath或pathType或webPath:
* 文件路径,传其中之一参数即可;
* filePath是磁盘的绝对路径;
* pathType是路径类型,对应webconfig.properties中的key
* webPath是相对web应用的路径,如/spa
* fileRealName:下载保存后的文件名称,可选;如果为空则与fileName一样
*
* 2.后台导出文件下载,需传属性:
* fileName:磁盘上文件的名称,必需
* fileRealName:下载保存后的文件名称,可选;如果为空则与fileName一样
*/
@RequestMapping("download")
public void download(HttpServletRequest request, HttpServletResponse response) {
String filePath = "";
String fileName = request.getParameter("fileName");
String fileRealName = "";
if (!Util.isNull(fileName)) {
filePath = request.getParameter("filePath");
if (Util.isNull(filePath)) {
String webPath = request.getParameter("webPath");
if (Util.isNull(webPath)) {
String pathType = request.getParameter("pathType");
filePath = WebconfigFactory.getValue(pathType);
} else {
//filePath = request.getRealPath(webPath) + System.getProperty("file.separator");
filePath = request.getServletContext().getRealPath(webPath) + System.getProperty("file.separator");
}
}
fileRealName = request.getParameter("fileRealName");
} else {//后台导出文件下载
fileName = (String) request.getAttribute("fileName");
filePath = WebconfigFactory.getTempFolder();
fileRealName = (String) request.getAttribute("fileRealName");
}

if (Util.isNull(fileRealName)) {
fileRealName = fileName;
}

BufferedInputStream bis = null;
OutputStream out = null;
try {
String userInfoHeader = "User-Agent";
String firefoxName = "firefox";
String urlFileName = "";
if (request.getHeader(userInfoHeader).toLowerCase().indexOf(firefoxName) > 0) {
urlFileName = new String(fileRealName.getBytes("UTF-8"), "ISO8859-1");
} else {
urlFileName = java.net.URLEncoder.encode(fileRealName, "UTF-8");
}

response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + urlFileName + "\"");
response.setHeader("Connection", "close");

bis = new BufferedInputStream(new FileInputStream(filePath + fileName), BUFFER_SIZE);
out = new BufferedOutputStream(response.getOutputStream(), BUFFER_SIZE);
byte[] buf = new byte[BUFFER_SIZE];
int len;
while ((len = bis.read(buf)) != -1) {
out.write(buf, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
FileUtil.close(bis);
FileUtil.close(out);
}
}


3.2、前台


function exp() {
let sql = "";//请自行设置
let col = "";//请自行设置
let title = "";//请自行设置
$.messager.progress({title : '处理中...'});//easyui控件,可用其他控件
$.getJSON(
"${pageContext.request.contextPath}/common/export/exportForCsv.do",
{
sql : sql,
col : col,
title : title,
separator: "|",
t : new Date().getTime()
},
function(data) {
$.messager.progress('close');//easyui控件,可用其他控件
if (data.returnCode == 0) {
location.href = "${pageContext.request.contextPath}/common/upDownload/download.do?fileName=" + encodeURI(data.result) + "&fileRealName=" + encodeURI("数据.csv") + "&pathType=path.temp.folder";
} else {
$.messager.alert('提示', "系统出错,错误信息为:" + data.description + ",请将该信息提供给代维人员寻求帮助");
}
}
);
}


3、公共类R

Ajax方式文件下载(后台返回为json或文件流)_控件 Ajax方式文件下载(后台返回为json或文件流)_数据_02


package com.inspur.common.entity;


/**
* 返回数据
*/
public class R<T> {
/**
* 返回码
* 0 正常,其他异常
*/
private int returnCode = 0;

/**
* 描述
*/
private String description = "OK";

/**
* 结果数据
*/
private T result;

public int getReturnCode() {
return returnCode;
}
public String getDescription() {
return description;
}
public T getResult() {
return result;
}

public static R ok() {
return new R();
}

public static <T> R<T> ok(T result) {
R<T> r = new R<>();
r.result = result;
return r;
}

public static <T> R<T> error() {
R<T> r = new R();
r.returnCode = -1;
r.description = "未知异常,请联系管理员";
return r;
}

public static <T> R<T> error(String description) {
R<T> r = new R();
r.returnCode = -1;
r.description = description;
return r;
}

public static <T> R<T> error(int returnCode, String description) {
R<T> r = new R();
r.returnCode = returnCode;
r.description = description;
return r;
}

}

R.java


注意例子里sql是从台传到后台,这样容易引起SQL注入攻击;严谨的做法应该是前台传参数,后台拼sql,并使用PrepareStatement来执行sql。




Qt 多CPU 如何多线程调用python qt 多线程 connect

connect(this, SIGNAL(ReceiveThread(void*,int,int)), receiveInfo, SLOT(doReceiveTh(void*,int,int))); connect(&receiveThread, SIGNAL(finished()),receiveInfo, SLOT(deleteLater()));在主线程做以上操作。上面中Re

python list在线格式化 python format list

1、Format格式化什么是format格式化?相对于基本格式化输出采用“%”的方法,format的功能强大,该函数把字符串当一个模板,通过传入的参数进行格式化,并且使用大括号“{}”作为特殊字符代替“%”format基本用法· 不带编号,即“{}” · 带数字编号,可调换顺序,即“{1}",“{2}” · 带关键字,即“{a}","{tom}"name = 'DDMM' gender = '女'