React/Redux 下载文件 [英] React/Redux download file
问题描述
我需要在单击按钮时从服务器下载文件.
我创建了一个 MaterialUI 按钮,在它的 onclick 回调中,我调用了容器组件 connected 的一个动作.
该操作是异步的并执行 ajax POST:
export const onXlsxClick = () =>调度 =>{const urlParams = {过滤器:{聚合:'macro_area',图表分辨率:'1_小时',chart_from: '1478080363',chart_to: '1477993963'},标签:['PROVA1','PROVA2'],系列: [{标签:空,时间戳:1478080363,值:[123, 345]},{标签:空,时间戳:1477993963,值:[153, 3435]}]};返回 $.ajax({url:'/rest/export/chart/xlsx',类型:'POST',数据类型:'应用程序/json',内容类型:'应用程序/json',数据:JSON.stringify(urlParams)}).完成(数据=> {console.log('成功');}). 失败(错误 => {控制台日志(错误);});};
服务器接收请求并通过此 REST 服务正确处理它:
@POST@Path("xlsx")@Produces("application/vnd.ms-excel")公共响应 getXlsx(ChartExportRequest 请求){ResponseBuilder responseBuilder;ChartExportRequestDTO reqDto = null;尝试 {reqDto = parseDTO(请求);checkRequestDTO(reqDto);ExportDTO dto = getXlsxProvider().create(reqDto);responseBuilder = Response.ok(dto.getFile()).header("Content-disposition", "attachment;filename=" + dto.getFileName());}捕获(异常 e){logger.error("为带有请求 [" + (reqDto != null ? reqDto.toString() : null) + "]" 的标签 RIGEDI 提供导出 xlsx 时出错,e);responseBuilder = Response.serverError().entity(e.getMessage());}返回 responseBuilder.build();}
问题是响应正确到达客户端,但没有任何反应:我希望浏览器显示下载对话框(例如:在 chrome 中,我希望下载的底部栏与我的文件一起出现).>
我做错了什么?
AS per Nate's answer 这里,Ajax 请求的响应不被浏览器识别为文件.对于所有 Ajax 响应,它的行为方式相同.您需要手动触发下载弹出窗口.
在我的实现中,我使用 filesaverjs 触发下载弹出窗口,一旦我收到减速器中的 API 响应.
由于 FileSaver 使用 blob 来保存文件,我将来自服务器的响应作为 blob 发送,将其转换为字符串数组缓冲区,然后使用它来保存我的文件.
中描述了这种方法请在下面找到减速器的示例代码:(根据 Redux,使用 reducer 进行状态修改)减速器.js
let fileSaver = require("file-saver");导出默认函数 projectReducer(state = {}, action){让项目;开关(动作.类型){案例 GET_PROJECT_SUCCESS :项目 = Object.assign(action.response.data);回归项目;案例 EXPORT_AND_DOWNLOAD_DATA_SUCCESS :让数据 = s2ab(action.response.data);fileSaver.saveAs(new Blob([data], {type: "application/octet-stream"}), "test.xlsx");返回状态;}返回状态;}函数 s2ab(s) {var buf = new ArrayBuffer(s.length);var view = new Uint8Array(buf);for (var i = 0; i != s.length; ++i) {视图[i] = s.charCodeAt(i) &0xFF;}返回缓冲区;}
I need to download a file from the server when a button is clicked.
I created a MaterialUI button and on its onclick callback i call an action of the container component connected.
The action is asynchronous and does an ajax POST:
export const onXlsxClick = () => dispatch => {
const urlParams = {
filters: {
aggregation: 'macro_area',
chart_resolution: '1_hour',
chart_from: '1478080363',
chart_to: '1477993963'
},
labels: ['PROVA1', 'PROVA2'],
series: [
{
label: null,
timestamp: 1478080363,
values: [123, 345]
},
{
label: null,
timestamp: 1477993963,
values: [153, 3435]
}
]
};
return $.ajax({
url:'/rest/export/chart/xlsx',
type: 'POST',
dataType: 'application/json',
contentType: 'application/json',
data: JSON.stringify(urlParams)
})
.done(data => {
console.log('success');
})
.fail(error => {
console.log(error);
});
};
The server receive the request and handle it correctly through this REST service:
@POST
@Path("xlsx")
@Produces("application/vnd.ms-excel")
public Response getXlsx(ChartExportRequest request) {
ResponseBuilder responseBuilder;
ChartExportRequestDTO reqDto = null;
try {
reqDto = parseDTO(request);
checkRequestDTO(reqDto);
ExportDTO dto = getXlsxProvider().create(reqDto);
responseBuilder = Response.ok(dto.getFile())
.header("Content-disposition", "attachment;filename=" + dto.getFileName());
}
catch(Exception e) {
logger.error("Error providing export xlsx for tab RIGEDI with request [" + (reqDto != null ? reqDto.toString() : null) + "]", e);
responseBuilder = Response.serverError().entity(e.getMessage());
}
return responseBuilder.build();
}
The problem is that the response arrives correctly to the client but then nothing happens: I am expecting that the browser shows the download dialog (example: in chrome I expect the bottom bar of downloads to appear with my file).
What am I doing wrong?
AS per Nate's answer here, the response of Ajax request is not recognised by a browser as a file. It will behave in the same way for all Ajax responses. You need to trigger the download popup manually.
In my implementation, I used filesaverjs to trigger the download popup, once I have received the API response in reducer.
Since FileSaver uses blob for saving the file, I am sending the response from server as a blob, converting it into string array buffer and then using it to save my file. This approach was described in
Please find the sample code below for the reducer : (using reducer for state modification, as per Redux) reducer.js
let fileSaver = require("file-saver");
export default function projectReducer(state = {}, action)
{
let project;
switch (action.type) {
case GET_PROJECT_SUCCESS :
project = Object.assign(action.response.data);
return project;
case EXPORT_AND_DOWNLOAD_DATA_SUCCESS :
let data = s2ab(action.response.data);
fileSaver.saveAs(new Blob([data], {type: "application/octet-stream"}), "test.xlsx");
return state;
}
return state;
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
这篇关于React/Redux 下载文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!