-1

I got an issue using fetch method in JavaScript with a NodeJS routing.

My JS code :

fetch(url, {
  method: 'POST',
  headers: {
    'Accept': 'application/json, text/plain, */*',
    'Content-Type': 'application/json'
  },
  credentials: 'same-origin',
  body: JSON.stringify({ ids: arrayIds })
}).then(function(response) {
  return response.json();
}).then(function(data) {
  if (data.reload) {
    document.location.reload();
  } else if (data.url) {
    document.location.href = data.url;
  }
});

My NodeJS route :

function _postMultiplePrint(request, response) {
  var fonts = {
        Roboto: {
          normal: 'public/fonts/OpenSans-Regular.ttf',
          bold: 'public/fonts/OpenSans-Bold.ttf',
          italics: 'public/fonts/OpenSans-Italic.ttf',
          bolditalics: 'public/fonts/OpenSans-BoldItalic.ttf'
        }
      },
      printer = new PdfPrinter(fonts),
      docDefinition, pdfDoc;

  docDefinition = {
    content: [
      {
        text: 'This paragraph fills full width, as th'
      }
    ],
    pageSize: 'A4',
    pageMargins: [72, 72],
    footer: {},
    background: {}
  };

  pdfDoc = printer.createPdfKitDocument(docDefinition);
  response.setHeader('Content-type', 'application/pdf');
  response.setHeader('Content-disposition', 'inline; filename="book.pdf"');
  pdfDoc.pipe(response);
  pdfDoc.end();
  response.send(JSON.stringify({ reload: false, url: '' }));
}

My issue : my book.pdf is not loaded in the page when I'm not using fs to write it.

It works with this code but I write files on the server and I don't want to do that :

function _postMultiplePrint(request, response) {
  var fonts = {
        Roboto: {
          normal: 'public/fonts/OpenSans-Regular.ttf',
          bold: 'public/fonts/OpenSans-Bold.ttf',
          italics: 'public/fonts/OpenSans-Italic.ttf',
          bolditalics: 'public/fonts/OpenSans-BoldItalic.ttf'
        }
      },
      printer = new PdfPrinter(fonts),
      docDefinition, pdfDoc;

  docDefinition = {
    content: [
      {
        text: 'This paragraph fills full width, as th'
      }
    ],
    pageSize: 'A4',
    pageMargins: [72, 72],
    footer: {},
    background: {}
  };

  pdfDoc = printer.createPdfKitDocument(docDefinition);
  pdfDoc.pipe(fs.createWriteStream('./public/uploads/book.pdf'));
  pdfDoc.end();
  response.send(JSON.stringify({ url: '/uploads/book.pdf' }));
}
tonymx227
  • 5,293
  • 16
  • 48
  • 91
  • 1
    You forgot to ask a question. What data is actually sent? What do you see in the network monitor? It looks like you're first sending the PDF and then a JSON string in the same response body. – jabaa Feb 17 '23 at 10:21

2 Answers2

1

Each HTTP request can have one and only one response.

Your JS makes an HTTP request. Your server-side code then tries to response with PDF (response.setHeader('Content-type', 'application/pdf'); response.setHeader('Content-disposition', 'inline; filename="book.pdf"'); pdfDoc.pipe(response); pdfDoc.end();) and JSON (response.send(JSON.stringify({ reload: false, url: '' }));).

That's two responses. That won't work.

What's more, the Content-disposition will be ignored because it is a request initiated by Ajax, not by regular browser navigation.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Ok but is there any solution without sending un new response ? I don't want to write the file. – tonymx227 Feb 17 '23 at 11:01
  • Make one request. Send one response. If you want the browser to handle the download automatically: Don't use Ajax. If you want to use Ajax: Write client-side JavaScript to handle saving the data in the response Ajax recieves. – Quentin Feb 17 '23 at 11:07
-1

The first backend code is fine, it's just not compatible with the frontend. Here's a full working example

backend

pdfDoc = printer.createPdfKitDocument(docDefinition);
response.setHeader('Content-type', 'application/pdf');
response.setHeader('Content-disposition', 'inline; filename="book.pdf"');
pdfDoc.pipe(response);
pdfDoc.end();

// The below code must be remove 
// since the response has been sent by pdfDoc.pipe(response);
// response.send(JSON.stringify({ reload: false, url: '' })); // <- remove this line 

Frontend if your route is POST method

const form = document.createElement("form");

// Define your route here
form.setAttribute("action", "http://localhost:8000");
form.setAttribute("method", "POST");

const btn = document.createElement("button");
btn.setAttribute("type", "submit");
form.appendChild(btn);
document.body.appendChild(form);
btn.click();

Frontend if your route is GET method

window.open("http://localhost:8000");
hungtran273
  • 1,180
  • 9
  • 11