React/Redux download file
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;
}
Related videos on Youtube
Marco Stramezzi
I am a young Software Developer who takes the first steps in the Java EE universe. At the moment I am working mainly with backend technologies such as EJB and JPA (in addition to CDI cross technology).
Updated on November 10, 2022Comments
-
Marco Stramezzi over 1 year
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?
-
atom217 over 5 yearsWhat is use of view variable in s2ab function if you are returning buf ?
-
abhishek1191 over 5 yearsMy bad! That's actually a part of actual code. I needed file contents in an array and that's what view was for!
-
IsmailS almost 3 yearsBut state is not changing right? Is that fine?