Create image NextJS + NodeJS - 404 This page could not be found - node.js

I'm creating images using this library https://github.com/frinyvonnick/node-html-to-image
The output folder is public (https://nextjs.org/docs/basic-features/static-file-serving)
Everything works well on Dev, but when I build the app and run in production mode, I can't have access to the image (the image is created) but I receive the message 404 This page could not be found.
const nodeHtmlToImage = require('node-html-to-image')
async function generateImage({ htmlContent }) {
const htmlFile = `<html>
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons"/>
<style>
body {
width: 600px;
height: auto;
}
</style>
</head>
${htmlContent}
</html>`
const image_name = crypto.randomBytes(20).toString('hex')
const relativePath = `output/${image_name}.png`
const output = `${process.env.SERVER_STATIC_URL}/${relativePath}`
return await nodeHtmlToImage({
output: `./public/${relativePath}`,
html: htmlFile,
}).then(() => {
return { image_src: output }
})
.catch((err) => {
console.log('err')
return err
})
}
export { generateImage }
How can I solve it?
Thanks!

Related

How to FTP upload a buffer (pdf buffer) using NodeJS?

I converted a HTML to pdf using html-pdf-node, but I am not find a way to store this PDF in my server using FTP.
My Code:
const html_to_pdf = require('html-pdf-node');
const generatePDF = () => {
// The test HTML file
let content = `
<html>
<head>
<title>Test Application</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h2>Just a Test</h2>
</div>
</body>
</html>
`;
// generating the PDF
let options = {
format: 'letter',
margin: {
right: '40px',
left: '40px'
}
};
let file = { content };
html_to_pdf.generatePdf(file, options).then((pdfBuffer) => {
console.log(pdfBuffer); // This is the pdfBuffer. It works because if I send this buffer to my email as an attachment, I can open the PDF
// How can I create and store a test.pdf file inside my server using FTP connection?
});
}
Thank you
Using ftp should do it - https://www.npmjs.com/package/ftp
Installation: npm i ftp and usage something like:
const Client = require("ftp");
// snip snip some code here
html_to_pdf.generatePdf(file, options).then((pdfBuffer) => {
const c = new Client();
c.on("ready", function () {
c.put(pdfBuffer, "foo.pdf", function (err) {
if (err) throw err;
c.end();
});
});
const secureOptions = {
host: "localhost",
port: 21,
user: "TheGreatPotatoKingOfEurope",
password: "5uper_5eekrit!"
};
c.connect(secureOptions);
});

Next.js intercept response

Here is snippet of what my server looks like
nextApp.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url!, true);
handleNextRequest(req, res, parsedUrl);
}).listen(port);
});
Once I get the response for the request, how can I append html comment or any tag basically?
What I wanted to do is, before and after body tag, I want to add some comments.
Current structure
<body></body>
What I want
<!--someContext-->
<body> some content </body>
<!--someMoreContext-->
You can do it by editing the _document special file of Next that used as a base template to the page.
// ./pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

puppeteer trigger ajax and get response data

I need to get what comes in response to an ajax request after clicking on a div
Here is my html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Js test</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/index.js"></script>
<script type="text/javascript">
var pt = 'dc37ecab5ca7675be79857f3657b773';
</script>
<style type="text/css">
#button{
cursor: pointer;
border: 1px solid black;
background: yellow;
width: 100px;
text-align: center;
line-height: 2;
}
</style>
</head>
<body>
<h1>Title text</h1>
<div id="button">
<div>get data</div>
<div id="result">*******</div>
</div>
</body>
</html>
index.js
jQuery.noConflict();
jQuery(document).ready(function($) {
$('#button').click(function(){
$.ajax({
url: "ajax.php",
data: {pt: pt},
success: function(data){
$('#button').addClass('activated');
$('#result').text(data);
}
});
});
});
ajax.php
<?php
if(isset($_GET['pt']) && !empty($_GET['pt']) && $_GET['pt'] == 'dc37ecab5ca7675be79857f3657b773'){
echo 'success';
}else{
echo 'error';
}
?>
When a button is pressed, a request is made the result of which is inserted into the result block, and a class 'activated' is added to the button block.
Next, I try to trigger this click through the puppeteer.
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const pageURL = 'http://puppeteer.local';
try {
await page.goto(pageURL);
console.log(`Page open: ${pageURL}`);
} catch (error) {
console.log(`Can not open page: ${pageURL} error: ${error}`);
}
const titleSelector = 'h1';
await page.waitForSelector(titleSelector);
const pageTitle = await page.$eval(
titleSelector, titleSelector => titleSelector.outerHTML
);
console.log('title: ', pageTitle);
const buttonSelector = '#button';
await page.waitForSelector(buttonSelector, { timeout: 0 });
await Promise.all([
//page.waitForNavigation(),
page.click(buttonSelector),
]);
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
const buttonSelectorReuslt = '#button.activated #result';
await page.waitForSelector(buttonSelectorReuslt, { timeout: 5000 });
const buttonResult = await page.$eval(buttonSelectorReuslt,
buttonSelectorReuslt => buttonSelectorReuslt.innerHTML);
console.log('result: ', buttonResult);
await browser.close();
process.exit()
})();
I see in the console
node .\index.js
Page open: http://puppeteer.local
title: <h1>Title text</h1>
result: success
Is it possible to somehow force the puppeteer to initialize the request on his own without pressing a button.
We need to do this GET request to the url with the parameter
http://puppeteer.local/ajax.php?pt=dc37ecab5ca7675be79857f3657b773
and get response.
and second question
How can I perform any action immediately after the end of the ajax request, now I'm just doing a delay of 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
And I would like to somehow find out that the ajax request has been completed and immediately after that do the remaining actions, such as searching for updated tags, or parsing what returned this request
In other words, something like a handler of the received response, rather than doing this delay in the execution of the code for 3 seconds

Download PDF from node API via FTP

I have PDF on a remote server. I have API with node and I want, from my website, download the PDF.
I'm using jsftp to upload and read PDF. It works fine :
let str = '';
FTP.get('path/to/my/file', (err, socket) => {
socket.on("data", d => {
str += d.toString();
});
socket.on("close", err => {
if (err) {
console.error("There was an error retrieving the file.", err);
}
// HERE I HAVE THE FILE IN STRING
});
socket.resume();
});
On the close event I have the file in String, but I don't succeed to send it to the browser. I've tried things like :
let s = new Readable();
s.push(str);
s.push(null);
s.pipe(res);
OR
res.end(str);
But nothing happen in browser
I'm using Polymer for my ajax request
<iron-ajax
id="openPdf"
content-type="application/json"
method="POST"
url="/api/karaweb/pdf"
on-response="handleOpenPdfResponse"
on-error="errorMessage"></webservice-request>
Any solutions ?
Thanks
I have a mixin called PdfDownloaderMixin; here is the whole code:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<dom-template id="pdf-downloader-mixin">
<script>
/* #polymerMixin */
const PdfDownloaderMixin = (superClass) => class extends superClass {
constructor() {
super();
}
downloadPdf(blobData) {
let fileObjectUrl = window.URL.createObjectURL(blobData);
window.open(fileObjectUrl);
}
}
</script>
</dom-template>
Then you use like this in your element:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html" />
<link rel="import" href="pdf-downloader-mixin.html" />
<dom-module id="attachment-handler">
<template>
<iron-ajax id="getAttachmentAjax"
url="[[rootPath]]api/session/getattachment"
debounce-duration="300"
handle-as="blob"></iron-ajax>
</template>
<script>
class AttachmentHandler extends PdfDownloaderMixin(Polymer.Element) {
static get is() { return 'attachment-handler'; }
getAttachment() {
this.$.getAttachmentAjax.generateRequest().completes.then((req) => {
req.succeeded && this.downloadPdf(req.response);
});
}
}
customElements.define(AttachmentHandler.is, AttachmentHandler);
</script>
</dom-module>

React Nodejs - Custom #font-face in jsx for PDF generation

I have a React project in which part of the functionality involves sending a request to a Nodejs server that runs PhantomJS and the html-pdf plugin in order to create a pdf that gets build via a react component from my node server. The problem I'm having is actually embedding custom fonts into my react component. I have a base64 encoded font set that would be easier to include versus a bunch of files, but due to how React loads in CSS, I keep getting errors.
Here is the function that gets called to generate the PDF on my NodeJS server:
exports.order = (req, res, next) => {
phantom.create().then(function(ph) {
ph.createPage().then(function(page) {
// page.viewportSize = { width: 600, height: 600 };
page.open(`http://localhost:3005/print/work/${req.params.id}`).then(function(status) {
page.property('content').then(function(content) {
pdf.create(content, options).toBuffer(function(err, buffer){
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename=order-${req.params.id}.pdf`,
'Content-Length': buffer.length
});
res.end(buffer);
});
page.close();
ph.exit();
});
});
});
});
}
Here's the actual React component that contains the HTML for the PDF:
var React = require('react');
var moment = require('moment');
class PrintWorkOrder extends React.Component {
render() {
var order = this.props;
var s = {
body: {
width: '100%',
float: 'none',
fontFamily: 'Helvetica',
color: '#000',
display: 'block',
fontSize: 10,
}
}
return (
<div style={s.body}>
I shortened the content in her for brevity
</div>
)
}
}
module.exports = PrintWorkOrder;
The functionality currently works, it's just now I need to add some custom fonts that aren't Helvetica and I'm having trouble solving that with this current implementation. Does anyone have any insight into this?
I also have the same issue, what I did is to encoded front into base64 and put it as
font.css
#font-face {
font-family: 'fontName';
src: url(data:application/x-font-woff;charset=utf-8;base64,<base64>) format('woff'),
font-weight: normal;
font-style: normal;
}
The trick is to read CSS file content and inject it into the DOM markup as style tag
React.createElement('style', { dangerouslySetInnerHTML: { __html: this.props.children } });
So to do it:
For the second line of code, what it does it to create style element. You could do is to write a component that read content from the .css file(s) and then put the content inside style element like this:
Style.js
var React = require('react');
export default class Style extends React.Component {
static propTypes = {
children: React.PropTypes.string.isRequired
};
render() {
return React.createElement('style', { dangerouslySetInnerHTML: { __html: this.props.children } });
}
}
Modify PrintWorkOrder.js
var React = require('react');
var moment = require('moment');
var fs = require('fs');
var path = require('path');
var Style = require('./Style');
const readFile = (filePath) => fs.readFileSync(path.resolve(__dirname, filePath)).toString();
class PrintWorkOrder extends React.Component {
constructor(props) {
super(props);
this.state = {
styles: []
}
}
componentWillMount() {
this.setState({
styles: [
readFile('font.css') // Assuming putting font.css same location with PrintWorkOrder component
]
})
}
render() {
var order = this.props;
var s = {
body: {
width: '100%',
float: 'none',
fontFamily: 'Helvetica',
color: '#000',
display: 'block',
fontSize: 10,
}
}
return (
<html>
<head>
{/* Here to render all the styles */}
{this.state.styles.map(s => (<Style>{s}</Style>))}
</head>
<body>
<div style={s.body}>
I shortened the content in her for brevity
</div>
</body>
</html>
);
}
}
module.exports = PrintWorkOrder;

Resources