Using Nodejs (fs) to access files selected with input type=file - node.js

I am having trouble reading files in different locations (other than the Node project directory). I need to read a private key file (could be located anywhere on the file system) and transfer some yaml/yml files (also could be located anywhere) via sftp. These files are selected in a file input field which is accessed by a post method in my index.js (in Node). The problem is when I try to read the files I get an error that the file doesn't exist, specifically because the directory defaults to the Node project directory.
For example:
D:/path/to/Node/project
D:/this/is/where/the/keyis
It will try and read this file:
D:/path/to/Node/project/keyis
I'm not sure why I only get the filename. I know receiving the entire path is a security risk but I sort of need the path, even if it's not printed at any stage.
This is the code for the html form:
<form id="file-upload" name="uploadform" method="post" action="/upload" onsubmit="update()">
<input type='text' id='username' class="btn btn-lg btn-default" name='username' placeholder='Enter Username' style="color:#000"><br>
<h3>Select Key:</h3><input type='file' id='key-select' name='keySelect'><br>
<h3>Select Yaml:</h3><input type="file" id="file-select" name="yamlfiles[]" multiple accept=".yml, .yaml"/><br>
<button type="submit" class="btn btn-lg btn-default" id="upload">Upload Data</button>
</form>
and then in the index.js:
router.post('/upload', function(req,res) {
// Create a new connection
var fs = require('fs');
var Connection = require('ssh2');
var c = new Connection();
// Get the files to be uploaded
var files = req.body.yamlfiles;
var uname = req.body.username;
var key = req.body.keySelect;
...
...
c.connect(
{
host: 'some_host',
port: 22,
username: uname,
privateKey: fs.readFileSync(key)
}
);
Essentially readFileSync isn't working unless 'key' is in the project path. Is there something that I am missing? The same happens with 'files'.

Okay, I seem to have solved my own problem. For anyone that faces this problem in the future I will give a rough outline to my solution.
I needed to add: enctype="multipart/form-data" to my form. I had previously tried this, but I didn't understand what I needed to change on the server side. Using the nodejs module: https://github.com/andrewrk/node-multiparty I could parse the form data into a readable object. My server side code then became something like this:
router.post('/upload', function(req,res) {
// Create a new connection
var fs = require('fs');
var multiparty = require('multiparty');
var util = require('util');
var Connection = require('ssh2');
var c = new Connection();
// Get the files to be uploaded
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
// Each element of the object is an array
console.log("form parsed");
// yamlfiles is an array anyway
var yamlfiles = files.yamlfiles;
// username is just a text field, so the 0th element is username
var uname = fields.username[0];
// files is a single file, so the 0th element is my key
var key = files.keySelect[0];
...
...
// access key with key.path (will be a fakepath)
fs.readFileSync(key.path);
// access name with key.originalFilename
console.log(key.originalFilename);
After a bit of fiddling around It works perfectly. I hope this helps anyone else that faces this problem in the future and thank you very much to those who offered me assistance.

Related

A way to persist the same/unique id generated for crypto.randomUUID in a user on nodeJS?

I'm developing a web app where I need to give the user a unique ID for him, I searched and used the crypto module to generate UUID and it works, here is the code for that (I'm sending it with my res.render of products).
import Product from '../models/Products';
const crypto = require('crypto');
const getProducts = async (req, res) => {
const products = await Product.find().lean();
const idValueUser = crypto.randomUUID();
console.log(idValueUser);
res.render('products/products', { layout: 'products-lyt',
products: products,
userId: idValueUser});
};
module.exports = getProducts;
I'm passing to handlebars file the userId by the render and in the javascript of the file I pass that Id to the local storage:
<div class="container-fluid products-container">
<div class="products-columns">
{{#each products}}
{{> products-partial }}
{{/each}}
</div>
<p id="awadetest">{{userId}}</p>
</div>
<script>
var testeo = document.getElementById("awadetest").innerHTML;
console.log(testeo);
localStorage.setItem("test", testeo);
</script>
The Id pass correctly to the localStorage (which I'll send to the database by a form after) but the problem is that every time the user reload (make a petition to the page of products) the id change (logically) but I need a way to make that Id persist so I can identify the user after on the Database, someone knows a way to do that? or if exist a better way to identify the user that doesn't imply log in or use the IP, btw thanks
If is helpful for someone I let the solution what works for me (easier that i thought):
Basically is to made a validation on the local storage (or cookie, if you're using it) like this:
window.onload = function () {
var testeo = document.getElementById("awadetest").innerHTML;
if (localStorage.getItem("userId") == null){
localStorage.setItem("userId", testeo);
}
else{
return;
}
}
With this easily the code can identify if the browser already have a userId (I tested on different browsers and it works, generate different codes for every browser). And if someone is on the same "problem" that me (identify users after in a shopping cart) you can add to your "form" that is sent to DB the userId taking it from the local storage, hope it helps someone

What am i missing for the file upload using multipart form data ?

Hi for whatever reason i cannot use packages e.g. multer to upload file to node server. So i found example online, if just upload file in the form, it works fine.
Now i want to send another field "password" together with the file during submit, just cannot make it a work.
I do know there're plenty modules out there, for now just want to this example to work.
<form style="height: 100%;padding-bottom:63px;">
<p>
<input type="file" class="FirmwareFile"
name="myUpload" file-model="upload.newFwFile">
</p>
<p>
<input type="password" name="password" id="password"
placeholder="Password"
ng-model="upload.controllerPassword"
class="formInput">
</p>
</form>
httpSvc.uploadToUrl(myFile, myPd, myServerIPAddress, myRoute) {...}
factory.uploadToUrl = function (fwFile, pd, myServerIp, myRoute) {
var fd = new FormData();
//fd.append('passwd', pd); // cannot pass password to server side ?
fd.append('file', fwFile); // only this works
var deferred = $q.defer();
var completeUrl = ......
$http.post(completeUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).success(function (data) {
deferred.resolve(data);
}).error(function () {
deferred.reject();
});
return deferred.promise;
}
in server side, where to extract the password info please ?
var UploadImage = function(req, res, callback){
var destFile = fs.createWriteStream(uploadDest + "mytest");
var fileSize = req.headers['content-length'];
req.pipe(destFile); //why not sth. like req.body.file.pipe() ?
...
};
Your form does not have
<form enctype="multipart/form-data" action="..." method="...">
...
</form>
You will be better off using node-formidable. The example works straight out of the box. You might also want to look into angularJS specific form upload directives that have been made. No sense in reinventing the wheel.
Cheers

Uploading a file and sending it to the backend with React and Node

I need to upload a file in react and send it to the Node backend.
Since I never worked with uploading and sending files before, this is a little troubling for me.
So far I found this:
// this creates a React component that can be used in other components or
// used directly on the page with React.renderComponent
var FileForm = React.createClass({
// since we are starting off without any data, there is no initial value
getInitialState: function() {
return {
data_uri: null,
};
},
// prevent form from submitting; we are going to capture the file contents
handleSubmit: function(e) {
e.preventDefault();
},
// when a file is passed to the input field, retrieve the contents as a
// base64-encoded data URI and save it to the component's state
handleFile: function(e) {
var self = this;
var reader = new FileReader();
var file = e.target.files[0];
reader.onload = function(upload) {
self.setState({
data_uri: upload.target.result,
});
}
reader.readAsDataURL(file);
},
// return the structure to display and bind the onChange, onSubmit handlers
render: function() {
// since JSX is case sensitive, be sure to use 'encType'
return (
<form onSubmit={this.handleSubmit} encType="multipart/form-data">
<input type="file" onChange={this.handleFile} />
</form>
);
},
});
Source: https://fitacular.com/blog/react/2014/06/23/react-file-upload-base64/
But now I basically just end up with some sort of string. But I need to send that file via REST to my Express backend, which needs to save that file in CouchDB.
What is the best/easiest way to accomplish that?
If you are using body-parser, know that it handles json and url encoded forms, not multipart data !
You should use an other module.
For more infos, give a look at : File uploading with Express 4.0: req.files undefined

Nodejs/Express noob update page view no reload

I am using Node v.0.10.29 on Win XP SP3.
I am new to Node/Express.
I am trying to learn Node/Express by duplicating a existing PHP project.
http://stevenjsteele.com/database/
The PHP project allows the user to build a list of items
from either the materials, tools or equipment tables needed for a project,
without a page reload.
What I am having a hard time with (don't understand) is:
With the PHP project I can change tables using the select table
drop down without a page reload. This is done with a PHP echo and:
xmlHttp.open("POST","getpage.php?tablename="+str,true);
I initialize the app with:
var express = require('express'),
request = require('request'),
requirejs = require('requirejs'),
mysql = require('mysql'), // node-mysql module
path = require('path'),
bodyParser = require('body-parser'),
app = express();
var mh_connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password: ''
});
mh_connection.connect();
var materialhandler = router.route('/materialhandler');
materialhandler.get(function(req,res){
selecttools = 'tools';
selectelectrical = 'electrical';
selectequipment = 'equipment';
mh_connection.query('use materialhandler');
var strQuery = 'SELECT * FROM materialhandler.tools ORDER BY item_id';
mh_connection.query( strQuery, function(err, rows){
if(err) {
throw err;
}else{
tablename='Tools';
res.render('materialhandler',{title:"page title",data:rows});
res.end("");
}
});
});
In my node/express template (which is html) I use:
For the select option dropdown inside of a form,
I use: onchange="getData()"
<form id="theForm" action="/materialhandler">
<div class="tablepicker two-thirds column">
<select id="selectTable" onchange="getData()" class="selectpicker" name="selectpicker">
<optgroup>
<option name="" value="">Select Table</option>
<option name="tools" value="tools"><%=selecttools%></option>
<option name="electrical" value="electrical"><%=selectelectrical</option>
<option name="equipment" value="equipment"><%=selectequipment%</option>
</optgroup>
</select>
</div>
</form>
function getData() {
//console.log('begin');
var http = new XMLHttpRequest();
var selectedTable = selectTable.options[selectTable.selectedIndex].value;
http.open("POST", 'http://localhost:3000/materialhandler?selectpicker='+selectedTable, true);
http.setRequestHeader("Content-type", "application/x-www-form urlencoded");
http.onreadystatechange = function() {
console.log('onreadystatechange');
if (http.readyState == 4 && http.status == 200) {
//alert(http.responseText);
}
else {
console.log('readyState=' + http.readyState + ', status: ' + http.status);
}
}
//console.log('sending...')
http.send(selectedTable);
console.log(selectedTable)
//console.log('end');
$('#theForm').submit();
}
In terminal window console.log shows the selected table.
I am trying to change the tables without a page reload.
As I said at beginning I am just starting to learn this paradigm.
Any help or pointing in the right direction is appreciated.
Thanks.
The $('#theForm').submit(); is what's causing your page reload, but instead of doing it that way you can make your post request directly with javascript. jQuery would be a better alternative to XMLHttpRequest.
Instead of your getData() function, you can hook up some javascript to listen for select changes, and post them to your server like so:
$('#selectTable').on('change', function() {
$.post("materialhandler?selectpicker=" + $(this).val());
}

Formidable upload file don't trigger

Development and test environment:
Windows 8 64bit
node version installed 0.10.5
npm version 1.2.18
express framework
formidable module used for file uploading
Firefox and Internet Explorer browsers
HTML code:
<form id="caricaMasterImg" method="post" enctype="multipart/form-data" action="/image/upload">
<input type="file" id="masterImg" name="masterImg" value="Sfoglia" accept="image/*"/>
<input type="submit" value="carica" id="submitLoadImg" />
</form>
app.js code:
var image = require('./route/image');
app.post('/image/upload', image.upload);
routes/image.js code:
exports.upload = function(req, res){
var form = new formidable.IncomingForm();
form.uploadDir = path.join(__dirname, 'tmp');
console.log('Upload directory is: '+form.uploadDir);
fs.exists(form.uploadDir, function (exists) {
console.log('is an existing directory? '+exists);
});
form.parse(req, function(err, fields, files) {
console.log(fields);
console.log(files);
});
};
The issue:
When submit button is clicked I expect to see the file logged with console.log(files) instruction. Log writes:
Upload directory is: C:\Liber-I\app\FileSystemManager\routes\tmp
is an existing directory? true
No more log is written on application console for several minutes.
I test the case of no-file submission and it seems it is acting the same! It is a too weired behaviour to be a nodejs problem, where am I doing wrong?
I think I did nothing wrong. Truth is I am not able to fix this problem, so I decided for a good work around. I choose to use connect-multiparty for file uploading and it is working just great.

Resources