res.render an ember route with express - node.js

I currently have a webapp using Express, Node, Ember, and Mongo. Ember app resides in a project folder (./public/index.html) in the root of the node/express install. I have express set to serve static files from the public directory and render index.html to any get requests so the ember app is accessible.
I have a route/view in my Ember app that has a form that accepts a file upload, this form's action is a post request to an express route that performs calculations and queries a local mysql database with updates. The function works fine but at the end of the .post express function when I res.json or res.send the response appears in the browser window and clears out my ember view.
I assume the correct way to handle this is to res.render('view',{data:'Finished processing file'});
then display the data value on the ember template. Question is how can I render an ember view with express. I added express-handlebars to my project and setup the view engine correctly but I don't know how to associate ember views with express so it knows how to render the correct view with response data.
hbs file for the ember view
<div class="col-md-8 col-md-offset-2 text-center">
<h2 class="toolTitle">Reactivate SKUs</h2>
<p class="lead">CSV Should Contain 1 Column (SKU) Only</p>
<form action="/tools/sku/reactivate" method="POST" enctype="multipart/form-data">
<input class="center-block" type="file" name="csvdata">
<button type="submit" class="btn btn-md btn-danger">Submit</button>
</form>
</div>
router.js(express router)
var quotes = require('../api/quote');
var cors = require('cors');
var sku = require('../api/tools/sku');
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
var util = require("util");
var fs = require("fs");
var corsOptions = {
origin: 'http://localhost:4200'
}
module.exports = function(router){
router.route('/quotes').post(cors(corsOptions),function(req,res){
console.log(req.body);
quotes.addQuote(req,res);
}).get(cors(corsOptions),function(req,res){
quotes.getAllQuotes(req,res);
});
router.route('*').get(cors(corsOptions), function(req,res){
res.sendFile('public/index.html',{root:'./'});
});
router.route('/tools/sku/reactivate').post(upload.single('csvdata'),function(req,res){
console.log('handing request over to sku.reactivate');
sku.reactivate(req,res);
});
};
sku.js express function
var mysql = require('mysql');
var csv = require('csv-parse');
var multer = require('multer');
var fs = require('fs');
//mysql setup
const connection = mysql.createConnection(
{
host : 'localhost',
user : 'rugs_remote2',
password : 'ofbiz',
database : 'rugs_prd2',
multipleStatements: true
}
);
connection.connect();
module.exports.reactivate = function(req,res){
//define mysql query function for once readStream emits end event
function reactivationQuery(arr){
console.log(arr);
const queryString = "UPDATE PRODUCT SET SALES_DISCONTINUATION_DATE = NULL WHERE PRODUCT_ID IN (?)";
connection.query(queryString,arr,function(err,rows,fields){
console.log(rows,fields);
if(err){
console.log('Error running sku.reactivate module error is: '+err);
}
res.send('DONE');
});
}
//define array for holding csv data in this case skus
const skuArray = [];
//define filesystem readstream from uploaded file
let readStream = fs.createReadStream(req.file.path).pipe(csv());
//push csv data to array ignoring headers to skuArray
readStream.on('data', function(chunk){
if(chunk[0] !== 'SKU'){
skuArray.push(chunk[0]);
}
});
//error handling
readStream.on('error',function(err){
console.log('Error while reading file stream [ERROR] '+ err);
res.send('Error while processing file');
});
//once read is finished map skuArray to usable string for IN Clause
readStream.on('end',function(){
let stringifyArray = skuArray;
stringifyArray = [stringifyArray];
reactivationQuery(stringifyArray);
});
}

Figured this out thanks to Max's help in the comments.
I was doing a full post request on form submit instead of using ajax to make the request this is why I was being routed out of my ember app and express would just render the response to the upload in the browser.
I installed ember-uploader again which was originally giving me issues because I didn't set the paramName option to match my file input name. Updated code below incase anyone else is running into a similar issue.
Ember HBS Template:
<div class="col-md-8 col-md-offset-2 text-center">
<h2 class="toolTitle">Reactivate SKUs</h2>
<p class="lead">CSV Should Contain 1 Column (SKU) Only</p>
<p class="lead flash-msg"></p>
{{file-upload id="upload" url="/tools/sku/reactivate" class="center-block" name="csvdata"}}
</div>
file-upload.js (ember-uploader component)
import Ember from 'ember';
import EmberUploader from 'ember-uploader';
export default EmberUploader.FileField.extend({
filesDidChange: function(files) {
const uploader = EmberUploader.Uploader.create({
url: this.get('url'),
paramName: 'csvdata'
});
if (!Ember.isEmpty(files)) {
alert(JSON.stringify(files));
// this second argument is optional and can to be sent as extra data with the upload
uploader.upload(files[0]).then(data => {
$('#upload').fadeOut('slow', function(){
$('.flash-msg').text(data);
$('.flash-msg').fadeIn('slow');
});
}, error => {
$('.flash-msg').text('Error uploading file please contact Jay: ' + error);
$('.flash-msg').fadeIn('slow');
});
}
}
});
express router
var quotes = require('../api/quote');
var cors = require('cors');
var sku = require('../api/tools/sku');
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
var util = require("util");
var fs = require("fs");
var corsOptions = {
origin: 'http://localhost:4200'
}
module.exports = function(router){
router.route('/quotes').post(cors(corsOptions),function(req,res){
console.log(req.body);
quotes.addQuote(req,res);
}).get(cors(corsOptions),function(req,res){
quotes.getAllQuotes(req,res);
});
router.route('*').get(cors(corsOptions), function(req,res){
res.sendFile('public/index.html',{root:'./'});
});
router.route('/tools/sku/reactivate').post(upload.single('csvdata'),function(req,res){
console.log('handing request over to sku.reactivate');
sku.reactivate(req,res);
});
};
express api function module:
var mysql = require('mysql');
var csv = require('csv-parse');
var multer = require('multer');
var fs = require('fs');
//mysql setup
const connection = mysql.createConnection(
{
host : 'localhost',
user : 'rugs_remote2',
password : 'ofbiz',
database : 'rugs_prd2',
multipleStatements: true
}
);
connection.connect();
module.exports.reactivate = function(req,res){
//define mysql query function for once readStream emits end event
function reactivationQuery(arr){
const queryString = "UPDATE PRODUCT SET SALES_DISCONTINUATION_DATE = NULL WHERE PRODUCT_ID IN (?)";
connection.query(queryString,arr,function(err,rows,fields){
if(err){
console.log('Error running sku.reactivate module error is: '+err);
res.json(err);
}
res.json('Successfully reactivated '+rows.changedRows+' SKUs');
});
}
//define array for holding csv data in this case skus
const skuArray = [];
//define filesystem readstream from uploaded file
let readStream = fs.createReadStream(req.file.path).pipe(csv());
//push csv data to array ignoring headers to skuArray
readStream.on('data', function(chunk){
if(chunk[0] !== 'SKU'){
skuArray.push(chunk[0]);
}
});
//error handling
readStream.on('error',function(err){
console.log('Error while reading file stream [ERROR] '+ err);
res.json('Error while processing file');
});
//once read is finished map skuArray to usable string for IN Clause
readStream.on('end',function(){
let stringifyArray = skuArray;
stringifyArray = [stringifyArray];
reactivationQuery(stringifyArray);
});
}

Related

Why my ctx.request.files is undefined, while ctx.request.body is ok?

I'm trying to parse data from my simple form using koa-body, but in post request handling I only can access ctx.request.body , but not ctx.request.files. I was trying several options t solve this, but it's not working out, ctx.request.files is empty in anyways. Wrote code according this example:koa-body/examples/multipart.js . There is code:
reg.js
const router = require('koa-router')();
const bodyParser = require('koa-body')({multipart:true});
router.post('/reg', bodyParser, async (ctx) => {
console.dir(ctx.request);
});
module.exports = router;
reg.pug
form(method='POST' action='/reg')
label Логин
input(type="text" id="login" name="login")
label Почта
input(type="text" name="mail")
label Пароль
input(type="password" name="password")
label Специализация
input(type="text" name="specialism")
label Пол
select(name="sex")
option(value="male") Мужрской
option(value="female") Женский
label Фото
input(type="file" name="image")
button(type="submit" value="Sign up") Sign Up
app.js
const Koa = require('koa');
const Pug = require('koa-pug');
const serve = require('koa-static');
const path = require('path');
const logger = require('koa-morgan');
const mongoDB = require('./config/database');
const homeRoute = require('./routes/home');
const regRoute = require('./routes/reg');
const app = new Koa();
// Connection to Mongoose
mongoDB.connect();
app.use(logger('dev'));
// Error-middleware handler
app.use(async(ctx, next) => {
try {
await next();
const status = ctx.status || 404;
if (status === 404) {
ctx.throw(404)
}
} catch (err) {
ctx.status = err.status || 500;
pug.locals.status = ctx.status;
if (ctx.status === 404) {
//Your 404.jade
await ctx.render('404error', pug.locals)
} else {
//other_error jade
await ctx.render('index', pug.locals)
}
}
});
app.use(serve(`${__dirname}/public`));
const pug = new Pug({
viewPath: path.resolve(__dirname, './views'),
locals: { },
app: app
});
app.use(homeRoute.routes());
app.use(regRoute.routes());
app.listen(3000, function(){
console.log('Server running on https://localhost:3000')
That's what ctx.request.body contains:
body: {
login: 'check',
mail: '123',
password: '123',
specialism: 'check',
sex: 'male',
image: 'SnWyoGZWgDA.jpg'
}
There are two ways of implementing koa-body within your app:
Quick Start (Not Recommended)
const Koa = require('koa')
const koaBody = require('koa-body')
const app = new Koa()
// Apply Koa-Body To All Routes
app.use(koaBody())
app.listen(3000)
Usage with koa-router
If you are using a router that supports middleware composition you should apply koa-body to only required routes.
const Koa = require('koa')
const Router = require('koa-router')
const KoaBody = require('koa-body')({multipart:true})
const app = new Koa()
const router = new Router()
// Apply Koa-Body to this route
router.post('/', KoaBody, async ctx=> {
console.log(ctx.request.body)
console.log(ctx.request.files)
})
app.use(router.routes())
app.listen(3000)
I have tested koa-body#4.1.1 and both methods work with the above examples.
Troubleshooting
If the ctx.request.files object is undefined, make sure your post form is set to encode the post data in multipart/form-data. This is very important if the form includes a file upload.
<form action="/" method="post" enctype="multipart/form-data">
<input class="file" id="file" type="file" name="file">
<input type="submit" value="Submit">
</form>
I was having the same issue in a new project. This is an issue in the new version of koa-body.
I changed koa-body version from "^4.1.1" to "^2.5.0". Now it is working fine.
To update use command:
npm remove koa-body
The install older version:
npm install koa-body#2.5.0
Now it will work fine.

MongoDb cluster connection with React Native App

I am very new to React Native. As part of my learning i was trying to connect a Mongodb Atlas cluster to my RN App. But I am very confused in developing the code for a post request to the Mongodb cluster. I was following a tutorial to develop a sample taxi booking app. It would be great if someone helps me to understand the code.
So my server code is as follows:
var express = require("express");
var path = require("path");
var bodyParser = require("body-parser");
var index = require("./routes/index");
var bookings = require("./routes/bookings");
var app = express();
var port = 3000;
app.listen(port,function(){
console.log("Server running on port",port);
})
//views
app.set("views", path.join(__dirname,"views"));
app.set("view engine","ejs");
app.engine("html", require("ejs").renderFile);
//Body Parser MW
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}))
//Routes
app.use("/",index);
app.use("/api",bookings);
Then i have a bookings.js file as follows:
var express = require("express");
var router = express.Router();
var MongoClient = require("mongodb").MongoClient ;
var uri = "mongodb+srv://TheCarAdmin:admin0000#thecarcluster-3hqxd.mongodb.net/test?&w=majority";
router.post("/bookings",function(req,res,next){
var bookings =req.body.data;
if(!bookings.username){
res.status(400);
res.json({
error:"Bad data"
});
}
else {
MongoClient.connect(uri)
.then(client=>{
const db = client.db("TheCar");
const coll = db.collection("bookings");
coll.save(bookings,function(err,savedBooking){
if(err){
res.send(err);
}
res.json(savedBooking);
});
})
.catch(err=>console.log(err));
}
});
module.exports = router;
In my App code i have a function that handles logging the booking request to the MongoDB collection as follows:
import update from "react-addons-update";
import constants from "./actionConstants";
import Geolocation from "react-native-geolocation-service";
import {Dimensions} from "react-native";
import RNGooglePlaces from "react-native-google-places";
import request from "../../../util/request";
import calculateFare from "../../../util/fareCalaculator";
export function bookCar(){
return(dispatch,store)=>{
const payload = {
data:{
username:"eman",
pickUp:{
address :store().home.selectedAddress.selectedPickUp.address,
name : store().home.selectedAddress.selectedPickUp.name,
latitude: store().home.selectedAddress.selectedPickUp.location.latitude,
longitude: store().home.selectedAddress.selectedPickUp.location.longitude
},
dropOff:{
address :store().home.selectedAddress.selectedDropOff.address,
name : store().home.selectedAddress.selectedDropOff.name,
latitude: store().home.selectedAddress.selectedDropOff.location.latitude,
longitude: store().home.selectedAddress.selectedDropOff.location.longitude
},
fare: store().home.fare,
status:"pending"
}
}
request.post("http://172.20.10.2:3000/api")
.send(payload)
.finish((err,res)=>{
dispatch({
type : BOOK_CAR,
payload : res.body
});
console.log(err.message);
console.log(res.status);
});
};
}
So as shown above the Request is imported from the following file:
const request = require("superagent");
const defaultAjaxTimeout = 30000;
//const cookie = require("cookie");
request.Request.prototype.finish = function (callback) {
// this replaces superagent's .end() function to include our custom error handling (see above)
this.end((err,res)=>{
callback(err,res);
});
};
var requestWrapper = function(method) {
// this is here so that we can append the .timeout call to all of our ajax requests with the default value.
return function(url) {
return request[method](url)
.type("form")
.timeout(defaultAjaxTimeout);
};
};
export default {
get: requestWrapper("get"),
put: requestWrapper("put"),
post: requestWrapper("post"),
del: requestWrapper("del"),
};
When i was debugging I realised that its not getting redirected to the booking.js code to process the POST request and hence throwing error. Could anyone help me to understand how this gets redirected to bookings.js or correct me if the written code is wrong.
Thanks in advance!!!!!

Ionic native file transfer - File upload - Node js express server but req.files undefined

I am a beginner at ionic framework developing.
This is flow of my ionic app.
- Select image from folders and press "upload a picture" button.
- I used ionic-native-file transfer for uploading to Nodejs express server.
This is my code.
//ionic page
https://www.dropbox.com/s/k1nittp0p8t4ay3/item-create.rar?dl=0
//Node js source
https://www.dropbox.com/sh/0zd9ydk0uhhz5g7/AABIg9S7hV6XiIzrMTj8FKA2a?dl=0
Main Point:
app.post('/upload', function(req,res)) , uploadImage()
//ionic3-item.js
uploadImage() //When press upload button
{
const fileTransfer:FileTransferObject = this.transfer.create();
let option: FileUploadOptions = {
fileKey:'file',
fileName:'name.jpg',
mimeType:'image/jpeg'
};
fileTransfer.upload(this.fileurl, encodeURI("http://192.168.1.249:8080/upload"),option);
}
}
//This Node js server code.
//route/ index.js
module.exports = function(app, Article)
{
//Uploaded Article------------------This part -------------------------
app.post('/upload', function(req,res){
console.log(req.files);
});
}
But req.files is undefined.
I wonder how I can treat the uploaded files from ionic app.
Please help.
Thanks.
This is client source.
var name = "upload";
let option: FileUploadOptions = {
fileKey:'file',
mimeType:'audio/3gp',
httpMethod:'POST',
fileName:'user_step4#'+name
};
this.loader = this.loadingCtrl.create({
content:'登录中...',
});
this.loader.present();
const fileTransfer:FileTransferObject = this.transfer.create();
console.log('filename'+this.curfilename);
fileTransfer.upload(this.file.externalRootDirectory+this.curfilename, encodeURI(localStorage.getItem('GlobalIP')+"/upload"),option).then((result)=>
{
console.log('success');
}).catch(error=>{
this.loader.dismiss();
console.log('uploaderror');
console.log(error.message);
});
}
This is server code
var multer = require('multer');
var storage = multer.diskStorage({
destination:function(req, file, cb)
{
console.log('uploadpath:'+file.originalname);
var pathname = file.originalname.split('#');
console.log(file.originalname);
var path = pathname[0].replace('_','/');
console.log(path);
cb(null,'public/resources/'+path);
},filename:function(req,file,cb)
{
var pathname = file.originalname.split('#');
var filename = pathname[1];
console.log(filename);
if(filename!=undefined)
cb(null, filename);
}
});
//For multipart/form-data Uploading
var upload = multer({storage:storage});
app.post('/upload',upload.single('file'), function(req,res,next)
{
console.log("uploaded");
res.json({result:1});
});
Thanks for reading.

TypeError : Cannot assign to read only property '_id' of { ... }

I am trying to acquire a keyboard string and save it in mongoDB, using the POST method. The server is started, but I can not write the input into the database, because the console returns the following error:
TypeError : Cannot assign to read-only property '_id' of {the text that I entered through html page}.
I started from very little to work with nodejs and mongodb, I need help ... Thanks for your attention :)
server.js:
const express = require('express');
const bodyParser= require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
var MongoClient = require('mongodb').MongoClient
var assert = require('assert');
var url = 'mongodb://localhost:27017/data';
// Use connect method to connect to the Server
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected correctly to server");
app.listen(3000, function() {
console.log('listening on 3000');
app.get('/index.html', function (req, res) {
res.sendFile( __dirname + "/" + "index.html" );
});
app.post('/process_post', function(req, res){
response={r:req.body.s};
var risp=JSON.stringify(response);
res.end(risp);
console.log("Insert : "+risp);
//"text" is an existing collection of mongodb "data" locally
var collection = db.collection('text');
collection.insert(risp);
});
});
db.close();
});
index.html:
<!DOCTYPE html>
<html>
<body>
<form action = "http://127.0.0.1:3000/process_post" method = "POST">
Input: <input "text" name=s > <br>
<button type = "submit">Submit</button>
</form>
</body>
</html>
The error happens because you are trying to add a string to the database. You should insert the object. Instead of inserting risp, try inserting the response var and see if it works. Also notice that the insert is asynchronous, so consider adding a callback function. See this example, the method is different but the logic should be the same.

Unzip file is not working

Im using the following code from
https://github.com/cthackers/adm-zip/wiki/ADM-ZIP-Introduction
Whant I need is to get a zip file from request(Im using express and I've request and response) and I need to extract(unzip) it to some path(in the example for my local drive) ,where should I put the req and what Im missing here to make it work
fn: function (req, res) {
var admZip = require('adm-zip');
var zip = new admZip();
zip.addLocalFile("C://TestFolder//TestZip");
in the request body im getting the zip file(im using postman and in the body I use the binary and select a zip file)
Please try my snippet code :
For some information, My App structure like this below :
my path --> C:\xampp\htdocs\service
service
|
-- tmp\
|
-- app.js
|
-- index.html
Client Side:
<html>
<body>
<h3>ZIP Upload:</h3>
<form action="/upload_zip" method="POST" enctype="multipart/form-data">
Select zip to upload:
<input type="file" name="zipFile" id="zipFile">
<input type="submit" value="Upload ZIP" name="submit">
</form>
</body>
</html>
Server Side:
Don't forget using enctype="multipart/form-data" when you post it using postman or something like that...
var express = require("express");
var fs = require("fs");
var AdmZip = require('adm-zip');
var app = express();
var multer = require("multer");
var multer_dest = multer({dest: "./tmp"}).single('zipFile');
app.get("/",function(req,res){
console.log("Show index.html");
res.sendFile(__dirname+"/"+"index.html");
});
app.post("/upload_zip",multer_dest,function(req,res){
console.log(req.file);
var zip = new AdmZip(req.file.path);
zip.extractAllTo("./tmp");
result = {
file:req.file,
message:"File has been extracted"
};
fs.unlink(req.file.path, function (e) {
if (e) throw e;
console.log('successfully deleted '+req.file.path);
});
res.end(JSON.stringify(result));
});
var server = app.listen(8081,function(){
var host = server.address().address;
var port = server.address().port;
console.log("Example App Listening at http://%s:%s",host,port);
})
Output :
You could simplify the problem by using form-data instead of binary and using multer. You can get the input file by accessing req.file after which you can perform your unzip operation.
For example, you would add to your route:
var upload = require('multer')({ dest: 'uploads/' });
var admZip = require('adm-zip');
app.post('/upload-here', upload.single('file'), function (req, res, next) {
var zip = new admZip(req.file.path);
zip.extractAllTo("C://TestFolder//TestZip", true);
});

Resources