I'm using the Parse server and I'm trying to send an email with an Html file. the problem I'm not really sure how to access the public folder from the cloud code.
This is the error:
ENOENT: no such file or directory, open './public/invoice.html'
The directory:
│ ├── cloud
│ │ ├── functions.js
│ │ ├── main.js
│ ├── public
│ │ ├── invoice.html
fs.readFileSync("../public/invoice.html", "utf8"
And this is my code:
var fs = require('fs');
Parse.Cloud.define("mailSend", function(request, response) {
const apiKey = '4441*****************a47f';
const mailgun = require("mailgun-js");
const DOMAIN = 'user.mailgun.org';
const mg = mailgun({apiKey: apiKey, domain: DOMAIN});
const data = {
from: 'email <email#example.com>',
to: 'email#example.com',
subject: 'Invoice',
html: fs.readFileSync("../public/invoice.html", "utf8") || null
};
mg.messages().send(data, function (error, body) {
console.log(body);
});
});
Try this:
var path = require('path');
var fs = require('fs');
Parse.Cloud.define("mailSend", function(request, response) {
const apiKey = '4441*****************a47f';
const mailgun = require("mailgun-js");
const DOMAIN = 'user.mailgun.org';
const mg = mailgun({apiKey: apiKey, domain: DOMAIN});
const data = {
from: 'email <email#example.com>',
to: 'email#example.com',
subject: 'Invoice',
html: fs.readFileSync(path.join(__dirname, "../public/invoice.html"), "utf8") || null
};
mg.messages().send(data, function (error, body) {
console.log(body);
});
});
Looks similar. Check this and try, Error: ENOENT: no such file or directory, unlink
Thank you all for your answers, This code works fine with me. I used ejs instead of HTML. you can use HTML but you have to add
var fs = require('fs'); insteead of const ejs = require('ejs');
var path = require('path');
const ejs = require('ejs');
const mailgun = require("mailgun-js");
const apiKey = '444**********00a47f';
const DOMAIN = 'user.mailgun.org';
const mg = mailgun({apiKey: apiKey, domain: DOMAIN});
Parse.Cloud.define("SendEmail", function(request, response) {
var orderId = request.params.orderId;
var items = request.params.items;
var details = request.params.details;
var user = request.params.user;
var subject = "Thank You for Your Order #" + orderId;
var orderData = {
user: user,
items: items,
details: details
}
ejs.renderFile(path.join(__dirname, './public/invoice.ejs'), orderData, (err, htmlString) => {
if (err) console.error(err);
let data = {
from: 'app neme <email#gmail.com >',
to: 'email#gmail.com',
subject: subject,
html: htmlString
};
mg.messages().send(data, function (error, body) {
console.log(body);
console.log(prods);
response.success("success send");
});
});
});
Related
I'm trying to embed an EJS template named 'ui.ejs' into handler.js.
The aim is to capture URL query parameters, then pass them to a function name 'ui.js' to capture data, that data is then passed onto the EJS UI template called 'ui.ejs'.
But I keep getting this error:
Failure: ENOENT: no such file or directory, open 'D:\Studio\Work\YellowCard\dynamo_serverless_rest\.webpack\service/views/ui.ejs'
Seems webpack is interfering with my directory paths.
The directory is under '/views/ui.ejs' NOT '.webpack\service/views/ui.ejs' like the error claims.
How do I fix it?
Thanks in advance..!
Here's the handler.js code:
// Import modules and dependencies
import {ui} from './index.js';
var ejs = require('ejs');
var fs = require('fs');
// Implementing the UI
export async function UserInterface(event, context, callback) {
// Capture data event parameters
const e = event.queryStringParameters;
// Get UI parameters
let params = await ui(e);
var htmlContent = fs.readFileSync(__dirname + '/views/' + 'ui.ejs', 'utf8');
var template = ejs.compile(htmlContent);
return {
statusCode: 200,
headers: { 'Content-type': 'text/html' },
body: JSON.stringify(template(params)),
};
};
You can use copy-webpack-plugin to copy the views folder to the destination directory.
In your webpack configuration file (webpack.config.js)
const CopyPlugin = require("copy-webpack-plugin");
// ...
plugins: [
new CopyPlugin({
patterns: [
{ from: "views", to: path.join(__dirname, '.webpack', 'service', 'views') },
],
}),
],
And also update serverless.yml file to include views directory to your lambda function
package:
include:
- views/**
The Fix:
I found a quick solution using the 'path' module.
import {ui} from './index.js';
var ejs = require('ejs');
var fs = require('fs');
const path = require('path');
// Implementing the UI
export async function UserInterface(event, context, callback) {
// Capture data event
const e = event.queryStringParameters;
// Get UI parameters
let params = await ui(e);
var htmlContent = fs.readFileSync(path.resolve(process.cwd(), './views/' + 'ui.ejs'), 'utf8');
var template = ejs.compile(htmlContent);
return {
statusCode: 200,
headers: { 'Content-type': 'text/html' },
body: JSON.stringify(template(params)),
};
};
I want to add mp3, lrc, wav, txt to the server and the folder name would be title and all of the extensions mentioned above would have specified names. As seen in the code, the mp3 would be "vocal.mp3".
addSong(event) {
jQuery('#addItemSave').text("Please wait!");
this._service.addSong(this.authorText, this.titleText, this.durationText, this.genre).subscribe(
res => {
this.data.push(res.data[0]);
this.addSongFilesToServer();
});
}
private addSongFilesToServer() {
this._service.addSongFilesToServer(this.titleText,
this.mp3file, this.lrcfile, this.pitchfile, this.thumbfile).subscribe(
res => {
console.log("Done! Now hide loading icon or what ever...");
jQuery('#addItemSave').text("Save");
jQuery('#addNewSongDialog').modal('hide');
jQuery('#addNewSongDialog').find("input,textarea,select")
.val('')
.end()
.find("input[type=checkbox], input[type=radio]")
.prop("checked", "")
.end();
});
}
addSongFilesToServer(title, mp3, lrc, txt, thumb) {
let url = this._config.ServerWithApiUrl + "uploadFiles";
let body : FormData = new FormData();
body.append('mp3', mp3, "vocal.mp3");
body.append('txt', txt, "pitches.txt");
body.append('lrc', lrc, "lyric.lrc");
body.append('thumb', thumb, "thumbnail.png");
body.append('title', title);
this._config.headers.delete('Content-Type');
return this._http.post(url, body, { headers: this._config.headers })
.map(res => res.json()).catch(function(err){
throw err;
});
}
addSong(event) is called when a button is pressed on the page and the all of the files that need to be passed on, are being passed on. The only problem here is that mp3, lrc, wav, txt go all under different folders with their their, for example vocal.mp3.
Just to illustrate, this is what I am getting at the moment :
├───songs
│ ├───vocal
│ │ vocal.mp3
│ ├───pitches
│ │ pitches.txt
...
And this is what I need:
├───songs
│ ├───title
│ │ vocal.mp3
│ │ lyric.lrc
│ │ pitches.txt
...
And the server side:
Router.post('/uploadFiles', (req, res) => {
var title = req.body.title || "title";
var storage = multer.diskStorage({
destination: function (req, file, cb) {
var dir = "../songs/" + file.originalname.split('.')[0];
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
cb(null, dir);
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
var upload = multer({ storage : storage}).any();
upload(req,res,function(err){
if(err){
return res.status(500).send({
code: 500, message: 'could not upload file: ' + err, error: err });
}else {
return res.status(200).send({
code: 200, message: 'All files uploaded!', err: ""});
}
});
});
You can see in the example that you actually coded that way
var storage = multer.diskStorage({
destination: function (req, file, cb) {
var dir = "../songs/" + file.originalname.split('.')[0];
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
cb(null, dir);
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
The file.originalname.split('.')[0] actually splits the extension and takes the filename.
so it will be var dir = "../songs/" + title; plain and simple.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
////////////////////////////
var dir = "../songs/" + title;
///////////////////////
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
cb(null, dir);
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
Also, body-parser doesn't handle multipart bodies, which is what FormData is submitted as. So the req.body.title will not work.
Check this for reference: How to handle FormData from express 4
Most probably you will need to send the title in another way
I am trying to write a file to a directory templates then stream a zip with the content that was written. However, the when the zip file is returned it says Failed - Network Error which is due to the fs.writeFile in the controller. If i remove the WriteFile stream then the zipping works fine. My question is how do i first write the file then run the zip. There seems to be something synchronous happening with the archiving and file writing of typeArrayString.
Controller:
exports.download_one_feed = function(req, res, next) {
Feed.findOne({'name': req.params.id})
.exec(function(err, dbfeeds){
if(err){
res.send('get error has occured in routes/feeds.js');
} else {
const feedArray = dbfeeds.feed.data;
// write file
// get from db collection & write file to download
const typeArrayString = JSON.stringify(feedArray);
let type = 'b'; // test only
fs.writeFile(path.join(appDir, 'templates/' + type + '/template.json'), typeArrayString, (err) => {
if (err) throw err;
console.log('Saved!');
})
archiverService.FileArchiver(feedArray, res);
}
})
};
Archive Service
const archiver = require('archiver')
const zip = archiver('zip')
const path = require('path')
const fs = require('fs')
const appDir = path.dirname(require.main.filename)
exports.FileArchiver = function (feedArray, res) {
// const app = this.app;
const uploadsDir = path.join(appDir, '/uploads/');
const templatesDir = path.join(appDir, '/templates/');
const extensions = [".jpg", ".png", ".svg"];
let imageArray = [];
const feedArrayObject = JSON.parse(feedArrayString);
feedArrayObject.forEach(function(x){iterate(x)}); // grab image names from object
imageArray = uniq_fast(imageArray); // remove duplicates
// zip images
for (let i = 0; i < imageArray.length; i++) {
console.log(imageArray[i])
const filePath = path.join(uploadsDir, imageArray[i]);
zip.append(fs.createReadStream(filePath), { name: 'images/'+imageArray[i] });
}
res.attachment('download.zip');
zip.pipe(res);
// zip template directory
console.log(templatesDir)
zip.directory(templatesDir, false);
zip.on('error', (err) => { throw err; });
zip.finalize();
return this;
}
Instead of writing the file then zipping the directory, i used zip.append to override the old file in the directory.
GOAL: Allow the user to download a PDF
Background: The below code generates a car.pdf file and stores it into the main project's directory when localhost:3000/ is loaded. This is great because I want to find a Car by id in the database, generate a handlebars template, pass the data from Car into it, and generate a PDF from the compiled HTML
Issue: Instead of saving the PDF to the main project's directory, I want it to download to the user's computer.
How can I do this?
Here is my code. I am using the NPM package: html-pdf
helpers/export-helper.js
const fs = require('fs');
const pdf = require('html-pdf');
const Handlebars = require('handlebars');
const { Car } = require('./../models/car');
var writePDF = (res) => {
Car.findById({_id: '58857e256b639400110897af'})
.then((car) => {
var source = fs.readFileSync(__dirname + '/templates/car.handlebars', 'utf8');
var template = Handlebars.compile(source);
var file = template(car);
pdf.create(file, { format: 'Letter' })
.toFile('./car.pdf', (err, res) => {
if (err) return console.log(err);
console.log(res); // { filename: '/app/businesscard.pdf' }
});
})
.catch((errors) => {
console.log(errors);
});
};
module.exports = { writePDF };
routes/home.js
const express = require('express');
const router = express.Router();
const { writePDF } = require('./../helpers/export-helpers');
router.get('/', (req, res) => {
writePDF();
});
module.exports = router;
You should use res.download for this. Like so
router.get('/', (req, res) => {
res.download('car.pdf');
});
https://expressjs.com/en/api.html#res.download
You have to pipe the created pdf with response to client side.
I want to load test.txt with nodejs.
var fs = require('fs');
fs.readFile('./test.txt', function (err, data) {
if (err) {
throw err;
}
console.log(data);
});
The path of the server is C:\server\test\server.js. The test.txt is located in the same directory, but I get this error: Error: ENOENT, no such file or directory 'C:\Users\User\test.txt'
Paths in Node are resolved relatively to the current working directory. Prefix your path with __dirname to resolve the path to the location of your Node script.
var fs = require('fs');
fs.readFile( __dirname + '/test.txt', function (err, data) {
if (err) {
throw err;
}
console.log(data.toString());
});
With Node 0.12, it's possible to do this synchronously now:
var fs = require('fs');
var path = require('path');
// Buffer mydata
var BUFFER = bufferFile('../test.txt');
function bufferFile(relPath) {
return fs.readFileSync(path.join(__dirname, relPath)); // zzzz....
}
fs is the file system. readFileSync() returns a Buffer, or string if you ask.
fs correctly assumes relative paths are a security issue. path is a work-around.
To load as a string, specify the encoding:
return fs.readFileSync(path,{ encoding: 'utf8' });
You should use __dirname to get the directory name the file is located instead of the current working directory:
fs.readFile(__dirname + "/test.txt", ...);
Use path and fs:
const fs = require("fs");
const pth = require("path");
Sync:
let data = fs.readFileSync(pth.join(__dirname,"file.txt"));
console.log(data + "");
A-Sync:
fs.readFile(pth.join(__dirname,"file.txt"), (err, data) => {
console.log(data + "");
});
And that; If you need to read the file continuously and send it to the client and the file size is not large, you may be able to keep a copy of it:
const exp = require("express");
const app = exp();
const fs = require("fs");
const pth = require("path");
let file = "";
app.get("/file", (q, r) => {
if (file === "")
file = fs.readFileSync(pth.join(__dirname,"file.txt")) + "";
r.writeHead(200, { "Content-Type": "text/plain" });
r.write(file);
r.end();
});
so if it is in the same directory just do this
fs.readFile(__dirname+'/foo.txt',function(e,d){console.log(d)})
If it's in same directory it should work. I have tested with the same code, with a file name.txt and it's working fine:
var fs = require('fs');
fs.readFile('./test.txt', function (err, data) {
if (err) {
throw err;
}
console.log(data.toString());
});