How to implement react-pdf with print function

16,999

Solution 1

Update on receiving the error message from the back-end:

When a request fails, we receive a JSON object from the back-end which contains the error message. The problem is that when we are forcing to receive the response in Blob format: responseType: 'blob' - no matter if the request fails or not, we receive a Blob Object. So, I was thinking about changing the responseType in the function provided from axios: transformResponse, but unfortunately, we do not have access to 'responseType' object, only to the headers. Here: https://github.com/axios/axios/pull/1155 there is an open issue about converting accordingly to responseType, it is still not resolved.

So, my way of resolving this problem is using fetch instead of axios. Here is an example:

fetch('here.is.your/endpoint', {
            method: 'POST', // specifying the method request
            body: JSON.stringify(request), // specifying the body
            headers: {
                "Content-Type": "application/json"
            }
        }
    ).then((response) => {
        if (response.ok) { // checks if the response is with status 200 (successful)
            return response.blob().then(blob => {
                const name = 'Report.pdf';
                saveAs(blob, name);
            });
        } else {
            return response.json().then((jsonError) => {
                this.setState({
                    modalMessage: jsonError.message // access the error message returned from the back-end
                });
            });
        }
    }).catch(function (error) {
        this.setState({
            modalMessage: "Error in data type received." // general handler
        });
    });

I hope this helps!

Solution 2

Recently I got a similar use case with the pdf part, my request is Post, but you can make it Get with no problem. So, what is happening:

1) - I am using axios for making a request to the back-end:

2) - request is the object that I am sending, but you will not have such, since you will probably send only id, for example: axios.get('here.is.your/endpoint/id');

3) - I am using: file-saver for saving the file I receive.

The rest of the code should be self-explaining and I also added some comments.

import {saveAs} from "file-saver";
...

axios.post('here.is.your/endpoint', qs.parse(request), {
       headers: {
          'Content-Type': 'application/json'
       },   
       responseType: 'blob' // here I am forcing to receive data in a Blob Format
    })
    .then(response => {
        if (response.data) {
            //Create a Blob from the PDF Stream
            const file = new Blob(
                [response.data],
                {type: 'application/pdf'});
            const name = 'Report.pdf';
            saveAs(file, name);
        } else {
            throw new Error("Error in data type received.");
        }
    })
    .catch(error => {
        this.setState({
            modalMessage: "Here Add Custom Message"
        });
   });

I cannot get the error message from the back-end still, I will text back if I get some progress on it - for now, I show a custom message.

I hope that helps!

Wish you luck!

Solution 3

I am glad I helped!

I have one more UPDATE FOR RECEIVING THE ERROR MESSAGE

THIS ONE IS VALID ONLY IF YOU RECEIVE TEXT MESSAGE NOT JSON

fetch('here.is.your/endpoint', {
        method: 'POST', // specifying the method request
        body: JSON.stringify(request), // specifying the body
        headers: {
            "Content-Type": "application/json"
        }
    }
).then((response) => {
    if (response.ok) { // checks if the response is with status 200 (successful)
        return response.blob().then(blob => {
            const name = 'Report.pdf';
            saveAs(blob, name);
        });
    } else {
         return response.text().then(function (error) {
                    throw new Error(error); // we should throw an Error with the received error
                }
            );
    }
}).catch(function (error) {
    this.setState({
        modalMessage: error.message // that way we access the error message
    });
});

We are using response.text().then() because of that way we manage to convert it from Promise to text. And it is important to use .then() because the Promise at that moment is resolved and we receive the Promise value. Then we simply throw an Error because we do not have access to the state object.

That is how you get a text from a response.

Share:
16,999
leonlai
Author by

leonlai

Updated on June 04, 2022

Comments

  • leonlai
    leonlai about 2 years

    I would like to use react-pdf to display the PDF and develop a printing function for direct printing (like using window.print());

    The REST server is developed using Jersey.

    The PDF will generate from server and return to React client using Jersey with return type is application/pdf. React client will display the PDF using react-pdf.

    I don't want to declare the URL path in "file" because this will retrieve the PDF from server again if the React state changed and triggered re-render. Also, I need to develop a print function to print the PDF displayed (Because the PDF content may change if retrieve the PDF again from server)

    Below show my code:

    Server:

    @Override
    @GET
    @Path("/pdf")
    @Produces(MediaType.APPLICATION_PDF_VALUE)
    public Response testPdf() throws Exception {
    
        File file = new File("C:\\Desktop\\test.pdf");
        FileInputStream fileInputStream = new FileInputStream(file);
        
        ResponseBuilder response = Response.ok((Object) fileInputStream);
        response.type("application/pdf");
        response.header("Content-Disposition", "filename=test.pdf");
        
        return response.build();
    }
    

    Client

    import React, { Component } from 'react';
    import { Document, Page } from 'react-pdf';
    import axios from 'axios';
    
    class MyApp extends Component {
        state = {
            numPages: null,
            pageNumber: 1,
            pdfContent: null
        }
    
        componentDidMount(){
            var that = this;
    
            axios.get("url\Pdf").then((response) => {
                 that.setState({pdfContent:response.data});
            }).catch((error) => {
                 console.warn(error);
            });
        }
    
        onDocumentLoadSuccess = ({ numPages }) => {
            this.setState({ numPages });
        }
    
        printHandler(){
            window.print();
        }
    
        render() {
            const { pageNumber, numPages } = this.state;
    
            return (
                <div>
                    <Document
                        file={this.state.pdfContent}
                        onLoadSuccess={this.onDocumentLoadSuccess}
                    >
                        <Page pageNumber={pageNumber} />
                    </Document>
                    <p>Page {pageNumber} of {numPages}</p>
    
                    <button onClick={() => this.setState(prevState => ({ 
                            pageNumber: prevState.pageNumber + 1 }))}>Next page</button>
                    <button onClick={() => this.setState(prevState => ({ 
                            pageNumber: prevState.pageNumber - 1 }))}>Prev Page</button>
    
                    <button onClick={this.printHandler}/>
                </div>
            );
        }
    }
    

    I want to get the PDF only one time and display the PDF using react-pdf. Also, I want to print the displayed PDF.

    I tried to convert the response.data to base64 followed this line because not success: (this will lose the pdf content) Encode PDF to base64 in ReactJS

    Code like:

        componentDidMount(){
            var that = this;
    
            axios.get("url\Pdf").then((response) => {
                let reader = new FileReader();
                var file = new Blob([response.data], { type: 'application/pdf' });
    
                reader.onloadend = () => {
                    that.setState({
                        base64Pdf:reader.result
                    });
                }
                reader.readAsDataURL(file);
            }).catch((error) => {
                 console.warn(error);
            });
        }
    

    Anyone can give me some suggestion? Or any better way to reach my goal?

    Thanks

    • Mikkel
      Mikkel over 5 years
      What have you tried so far?
    • leonlai
      leonlai over 5 years
      @Mikkel I have tried FileReader to convent response to base64. However, still not success(maybe I am not familiar on this...) I have updated some codes to my post.