如何从 javascript 中的服务器响应下载 XLSX 文件?
How to download XLSX file from a server response in javascript?
我有一个使用 Micronaut 框架的 React 前端和 java 服务器。在我的 Micronaut 框架的 BaseController
中,我正在创建一个 XLSX 文件,然后发送响应,以便在从前端代码调用时下载它。
基础控制器
@Inject ExportService exportService
@Controller('/abc/api/v1')
@Slf4j
class BaseController implements CachingHandler, CommonUtils {
@Post('/export/{name}')
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.ALL)
executeExporter(String name, @Nullable @Body LinkedHashMap payload) {
def handler = { LinkedHashMap paramMap ->
List paramMapList = paramMap.get("data") as List
Byte[] resultBytes = exportService.exportToXLSX(paramMapList, getFileName(getLookupValue(name), paramMap.get("key") as String), true)
log.info String.format("About to return XLSX file %s for %s",getFileName(getLookupValue(name), paramMap.get("key") as String), name)
return resultBytes
}
def results = handler.call(payload)
InputStream inputStream = new ByteArrayInputStream(results)
return new StreamedFile(inputStream, getFileName(getLookupValue(name), payload["key"] as String))
}
}
导出服务
注意:使用此 class 的主要方法,我能够创建一个 TestReport.xlsx
文件。 XLSXExporter
class 使用 Apache POI
库生成 XLSX 文件。
@Prototype
@Slf4j
class ExportService implements CommonUtils {
Byte[] exportToXLSX(List response, String fileName, boolean isDelete) {
def headers = []
try {
headers = response[0].keySet()
} catch(Exception e) {
e.printStackTrace()
}
Map params = [:]
params[Constants.HEADERS] = headers
params[Constants.DATA] = response
XLSXExporter xlsxExporter = new XLSXExporter()
boolean fileCreated = xlsxExporter.writeData(Constants.DEFAULT_SHEET_NAME, fileName, params)
if (fileCreated) {
Byte[] workbookContent = xlsxExporter.getFile(fileName, isDelete)
return workbookContent
} else {
return new Byte[0]
}
}
static void main(String[] args) {
def param = []
for (int i in 1..5) {
def paramMap = [:]
paramMap["key1"] = "data"+i
paramMap["key2"] = "data"+i
paramMap["key3"] = "data"+i
param.add(paramMap)
}
ExportService exportService = new ExportService()
Byte[] resultBytes = exportService.exportToXLSX(param, "TestReport.xlsx", false)
/**
I am able to create a proper TestReport.xlsx file using this main method
**/
}
}
我从 Postman 测试了上面的 API,它能够下载 XLSX 文件。
当我使用我的应用程序(java脚本)调用上述导出 API 时,它可以下载 xlsx 文件,但无法打开。
Javascript 调用导出的代码 API
const exportData = (filteredRows, activity) => {
let filename = "TestReport.xlsx";
return axios({
headers: {
'Content-type': 'application/json'
},
accept:'application/x-www-form-urlencoded',
url: '/abc/api/v1/export/'+ activity,
method: 'post',
data: {
"key": "8575",
"type":"userdetails",
"data":filteredRows
}
}).then(resp => {
var blob = resp.data;
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else{
var downloadLink = window.document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob([blob], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}));
downloadLink.download = filename;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
});
}
一旦我在上面的对话框中单击“是”,我就会收到以下错误消息,并且与从 Postman 下载的文件相反,不会显示任何内容。
我在我的项目中也遇到了同样的问题,但通过在您的请求的 header 中添加 responseType: "arraybuffer"}
来解决。
响应是一个包含二进制数据的 JavaScript ArrayBuffer。
您的 MS Excel (XLSX) 文件在服务器上生成并通过您的 API 发送回客户端。您可以使用 responseType = blob
将响应下载为文件。您还应该在浏览器上设置一个文件名,以便为下载的文件命名。
试试下面的代码片段。
const exportData = (filteredRows,activity) => {
let filename = "TestReport.xlsx";
let xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function() {
var a;
if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
a = document.createElement('a');
a.href = window.URL.createObjectURL(xmlHttpRequest.response);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
xmlHttpRequest.open("POST", '/abc/api/v1/export/'+ activity);
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
xmlHttpRequest.responseType = 'blob';
xmlHttpRequest.send(JSON.stringify({
"key": "8575",
"type":"userdetails",
"data":filteredRows
}));
}
从节点发送 excel 文件,然后我这样做了 -
axios({
url: 'http://api.dev/file-download',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf'); //or any other extension
document.body.appendChild(link);
link.click();
});
感谢@AabinGunz。我可以发表评论,因为我没有 50 的声誉。但他的片段有效。
我有来自之前设置的变量的 jsonString。下载的 xlsx 文件没有损坏。
再次感谢 AabinGunz
const exportData = () => {
let filename = userName+".xlsx";
let xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function() {
var a;
if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
a = document.createElement('a');
a.href = window.URL.createObjectURL(xmlHttpRequest.response);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
xmlHttpRequest.open("POST", '/calc-reverse-matches');
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
xmlHttpRequest.responseType = 'blob';
xmlHttpRequest.send(JSON.stringify(jsonString));
}
我有一个使用 Micronaut 框架的 React 前端和 java 服务器。在我的 Micronaut 框架的 BaseController
中,我正在创建一个 XLSX 文件,然后发送响应,以便在从前端代码调用时下载它。
基础控制器
@Inject ExportService exportService
@Controller('/abc/api/v1')
@Slf4j
class BaseController implements CachingHandler, CommonUtils {
@Post('/export/{name}')
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.ALL)
executeExporter(String name, @Nullable @Body LinkedHashMap payload) {
def handler = { LinkedHashMap paramMap ->
List paramMapList = paramMap.get("data") as List
Byte[] resultBytes = exportService.exportToXLSX(paramMapList, getFileName(getLookupValue(name), paramMap.get("key") as String), true)
log.info String.format("About to return XLSX file %s for %s",getFileName(getLookupValue(name), paramMap.get("key") as String), name)
return resultBytes
}
def results = handler.call(payload)
InputStream inputStream = new ByteArrayInputStream(results)
return new StreamedFile(inputStream, getFileName(getLookupValue(name), payload["key"] as String))
}
}
导出服务
注意:使用此 class 的主要方法,我能够创建一个 TestReport.xlsx
文件。 XLSXExporter
class 使用 Apache POI
库生成 XLSX 文件。
@Prototype
@Slf4j
class ExportService implements CommonUtils {
Byte[] exportToXLSX(List response, String fileName, boolean isDelete) {
def headers = []
try {
headers = response[0].keySet()
} catch(Exception e) {
e.printStackTrace()
}
Map params = [:]
params[Constants.HEADERS] = headers
params[Constants.DATA] = response
XLSXExporter xlsxExporter = new XLSXExporter()
boolean fileCreated = xlsxExporter.writeData(Constants.DEFAULT_SHEET_NAME, fileName, params)
if (fileCreated) {
Byte[] workbookContent = xlsxExporter.getFile(fileName, isDelete)
return workbookContent
} else {
return new Byte[0]
}
}
static void main(String[] args) {
def param = []
for (int i in 1..5) {
def paramMap = [:]
paramMap["key1"] = "data"+i
paramMap["key2"] = "data"+i
paramMap["key3"] = "data"+i
param.add(paramMap)
}
ExportService exportService = new ExportService()
Byte[] resultBytes = exportService.exportToXLSX(param, "TestReport.xlsx", false)
/**
I am able to create a proper TestReport.xlsx file using this main method
**/
}
}
我从 Postman 测试了上面的 API,它能够下载 XLSX 文件。
当我使用我的应用程序(java脚本)调用上述导出 API 时,它可以下载 xlsx 文件,但无法打开。
Javascript 调用导出的代码 API
const exportData = (filteredRows, activity) => {
let filename = "TestReport.xlsx";
return axios({
headers: {
'Content-type': 'application/json'
},
accept:'application/x-www-form-urlencoded',
url: '/abc/api/v1/export/'+ activity,
method: 'post',
data: {
"key": "8575",
"type":"userdetails",
"data":filteredRows
}
}).then(resp => {
var blob = resp.data;
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else{
var downloadLink = window.document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob([blob], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}));
downloadLink.download = filename;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
});
}
我在我的项目中也遇到了同样的问题,但通过在您的请求的 header 中添加 responseType: "arraybuffer"}
来解决。
响应是一个包含二进制数据的 JavaScript ArrayBuffer。
您的 MS Excel (XLSX) 文件在服务器上生成并通过您的 API 发送回客户端。您可以使用 responseType = blob
将响应下载为文件。您还应该在浏览器上设置一个文件名,以便为下载的文件命名。
试试下面的代码片段。
const exportData = (filteredRows,activity) => {
let filename = "TestReport.xlsx";
let xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function() {
var a;
if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
a = document.createElement('a');
a.href = window.URL.createObjectURL(xmlHttpRequest.response);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
xmlHttpRequest.open("POST", '/abc/api/v1/export/'+ activity);
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
xmlHttpRequest.responseType = 'blob';
xmlHttpRequest.send(JSON.stringify({
"key": "8575",
"type":"userdetails",
"data":filteredRows
}));
}
从节点发送 excel 文件,然后我这样做了 -
axios({
url: 'http://api.dev/file-download',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf'); //or any other extension
document.body.appendChild(link);
link.click();
});
感谢@AabinGunz。我可以发表评论,因为我没有 50 的声誉。但他的片段有效。 我有来自之前设置的变量的 jsonString。下载的 xlsx 文件没有损坏。 再次感谢 AabinGunz
const exportData = () => {
let filename = userName+".xlsx";
let xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function() {
var a;
if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
a = document.createElement('a');
a.href = window.URL.createObjectURL(xmlHttpRequest.response);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
xmlHttpRequest.open("POST", '/calc-reverse-matches');
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
xmlHttpRequest.responseType = 'blob';
xmlHttpRequest.send(JSON.stringify(jsonString));
}