React/Redux download file

13,102

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;
}
Share:
13,102

Related videos on Youtube

Marco Stramezzi
Author by

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, 2022

Comments

  • Marco Stramezzi
    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
    atom217 over 5 years
    What is use of view variable in s2ab function if you are returning buf ?
  • abhishek1191
    abhishek1191 over 5 years
    My bad! That's actually a part of actual code. I needed file contents in an array and that's what view was for!
  • IsmailS
    IsmailS almost 3 years
    But state is not changing right? Is that fine?