File upload using ftp and electron - node.js

I'm new to StackOverflow. I have started an electron project and I want to implement inside it the ability for the user to upload files using FTP. I've googled a bit and I've found a nice node module jsftp that is fit all my needs. The problem is that I'm new to electron and node coding, and I don't know how to manage the file upload. I've tried to use a standard file input but without success. My question is, how I can implement the file selection from the user and then upload the selected file to the FTP server?
Here is my code snippet:
HTML
<!-- Upload form row -->
<div class="row justify-content-center" id="">
<div class="col-8">
<form method="POST" action="/process-form" enctype="multipart/form-data" id="file-uploader">
<div class="form-row">
<div class="col-6">
<label for="file_name">File name</label>
<input type="text" class="form-control" id="file-name" name="file_name" placeholder="Nome file" />
</div>
<div class="col-6">
<label>Select file</label>
<div class="custom-file">
<input type="file" class="custom-file-input" name="uploaded_file" id="customFile" />
<label class="custom-file-label" for="customFile">Seleziona file</label>
</div>
</div>
<div class="col-12">
<label for="file_description">Description</label>
<textarea class="form-control" name="file_description" placeholder="Descrizione"></textarea>
</div>
<div class="col-12">
<button type="submit" name="upload_file" class="btn btn-link" id="process-form">UPLOAD</button>
</div>
</div>
</form>
</div>
</div>
JS code:
<!-- init app and xhr requests-->
<script type="text/javascript">
const jsftp = require('jsftp');
const bsCustomFileInput = require('bs-custom-file-input');
let ftp;
$(document).ready(function(){
$('#login-modal').modal('show');
$('#login-form').submit(function(e){
e.preventDefault();
var host = $('#host-field').val();
var user = $('#user-field').val();
var pass = $('#password-field').val();
ftp = new jsftp({
host: host,
port: 21, // defaults to 21
user: user, // defaults to "anonymous"
pass: pass, // defaults to "#anonymous"
});
ftp.list('public_html', (err, res) => {
console.log(res);
});
console.log(ftp);
});
bsCustomFileInput.init();
$('#file-uploader').submit(function(e){
console.log(ftp);
e.preventDefault();
var formData = new FormData($('#file-uploader')[0]);
var filename = $('#file-name').val();
ftp.put(formData, "mysubdomain.testing.net/"+filename+".jpeg", err => {
if(!err){
console.log("File transferred successfully!");
}
else{
console.log(err);
}
});
});
});
</script>

Related

Node js Dropzone js - Upload file without submitting form

I am new in node js and trying to integrate single file upload using dropzone js. I want to upload image before submitting form. I have managed to call the upload action before submitting.
Here is my html code:
<form id="add_user" name="add_user" class="form-horizontal" action="/add-new-user" method="POST">
<div class="directory-bg text-center">
<div class="directory-overlay">
<span id="imageuploader">
<input name="profileimg" id="profileimg" type="hidden" value="">
<img id="ws_user_avatar" class="rounded-circle thumb-lg img-thumbnail dropzone" data-thumb="public/assets/images/users/user.png" src="public/assets/images/users/user.png" alt="Generic placeholder image">
<!--<div class="fallback">
<input name="image" id="image" type="file">
</div>-->
x
</span>
</div>
</div>
<div class="directory-content p-4">
<div class="mt-4">
<% if(success != "") {%>
<div class="alert alert-success" role="alert">
<strong>Done!</strong> <%= success %>
</div>
<% } %>
<div class="row">
<div class="col-md-12">
<div class="mb-3">
<label class="form-label" for="email">Email Address</label>
<input type="email" class="form-control" name="email" id="email" placeholder="Email Address" data-key="Email Address" autocomplete="off" autofill="false">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label" for="fname">First Name</label>
<input type="text" class="form-control" name="fname" id="fname" placeholder="First Name" data-key="First Name" autocomplete="off" autofill="false">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label" for="lname">Last Name</label>
<input type="text" class="form-control" id="lname" name="lname" placeholder="Last Name" data-key="Last Name" autocomplete="off" autofill="false">
</div>
</div>
</div>
</div>
</div>
</form>
Here is the js code to handle dropzone event
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(".dropzone", {
url: '/upload-image',
autoProcessQueue: false,
uploadMultiple: false,
maxFiles:2,
maxFilesize: 2,
createImageThumbnails:false,
hiddenInputContainer: "img.rounded-circle",
init: function(file) {
var prevFile = null;
this.on("addedfile", function(file) {
var $this = this;
setTimeout(function () {
if(file.status == "error"){
alert(file.previewTemplate.innerText);
} else {
if(prevFile){
$this.removeFile($this.files[0])
}
prevFile = file;
$("#user_avatar").attr('data-imgname', file.name);
previewImage($this);
}
}, 10);
});
}
});
function previewImage(input){
let fileReference = input.files && input.files[0];
if(fileReference){
var reader = new FileReader();
reader.onload = (event) => {
document.getElementById('user_avatar').src = event.target.result;
}
reader.readAsDataURL(fileReference);
}
}
$(document).on('click','#btn_submit', function(e){
e.preventDefault();
if(myDropzone.getQueuedFiles().length > 0){
myDropzone.processQueue();
} else {
$("#add_user").submit();
}
});
Here is the post handler code in router.js file:
const express = require('express');
const router = express.Router();
router.post('/upload-image', function(req, res){
console.log('requested inage',req.files.file.path);
// fs.readFile(request.files.file.path, function(err, data) {
// var newPath = __dirname + "/public/img/xspectra/customlogo.png";
// fs.writeFile(newPath, data, function (err) {
// console.log("Finished writing file..." + err);
// response.redirect("back");
// });
// });
});
When the dropzone myDropzone.processQueue() function called, I get an error message in the post handler function TypeError: Cannot read property 'file' of undefined.
Can anyone suggest me where I am doing wrong?
Many Thanks!
That means that the file uploaded didn't properly make it to your web server. Are you using express-fileupload on the nodejs side to handle the uploads?
If so, I recommend checking out this link on a really great tutorial

Cannot read property 'heading' of null

My problem is I got an unexpected error at the browser end that says Cannot read property 'heading' of null
also marks this error at the client page file like about.ejs and showing; in the snap attached
I have provided all the required codes related to this. I reviewed multiple times to find for what or to where the actual errror originated but did not able to fix it.
codes of about.ejs
<%-include('./partials/header')%>
<div class="container-fluid">
<h1>About Section</h1>
<div class="card shadow mb-4">
<div class="card-header">
<form action="/admin/portfolio/create" method="post">
<button type="submit" class="btn btn-success">Edit About Page</button>
</form>
</div>
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">About Area</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<form action="/admin/about" method="post" >
<div class="form-group">
<label for="exampleFormControlInput1">Headings:</label>
<input class="form-control" type="text" name="heading" id="exampleFormControlInput1" value="<%=about.heading%>" >
</div>
<div class="form-group">
<label for="exampleFormControlInput2">Sub-Headings:</label>
<input class="form-control" type="text" name="subheading" id="exampleFormControlInput2" value="<%=about.subheading%>" >
</div>
<div class="form-group">
<textarea id="editor1" name="content" rows="10" cols="80"><%=about.content%></textarea>
</div>
<button type="submit" class="btn btn-primary">Save & Update</button>
</form>
</div>
</div>
</div>
<%-include('./partials/footer')%>
here is my router.js file
router.get('/admin/about',serverController.isAuthenticated,serverController.about)
router.post('/admin/about',serverController.isAuthenticated,serverController.about_post)
This is also my controller file
exports.about = async function(req,res){
res.render('server/about', {
about : await aboutCollection.findOne()
})
}
exports.about_post = function(req,res){
let about = new About(req.body)
about.create().then(async()=>{
res.redirect('/admin/about')
}).catch(()=>{
res.send('404')
})
}
and finally this all about my model of about page
const aboutCollection = require('../db').db().collection('about')
const objectId = require('mongodb').ObjectID
const About = function(about){
this.about = about
}
About.prototype.create = function(){
return new Promise(async(resolve,reject)=>{
await aboutCollection.updateOne({}, {$set :
{
heading : this.about.heading,
subheading : this.about.subheading,
content : this.about.content
}
})
resolve()
})
}
module.exports = About
it's because about sent value of none at the ejs tag i mean
<%= about.heading%>
Do,
exports.about = async function(req,res){
res.render('server/about', {
about : await aboutCollection.find().toArray()
})
}
Instead of,
exports.about = async function(req,res){
res.render('server/about', {
about : await aboutCollection.findOne()
})
}
In my case, I did this; after trying then see error has gone.

Cannot POST /admin Error return in web browser

I want to update my images on the blog page. but I got an error in the web browser like
Cannot POST /admin/portfolio/609911b1fba77be609396747/edit_cover
here is edit_cover.ejs file code and I use post method to submit images in the database
<%-include('./partials/header')%>
<div class="container-fluid">
<h1 class="text text-primary">Change Porfolio Cover Image Section</h1>
<div class="card shadow mb-4">
<div class="card-header">
<form action="/admin/portfolio/<%=work._id%>/edit_cover" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleFormControlFile1">Cover</label>
<input type="file" name="cover" class="form-control-file" id="exampleFormControlFile1">
</div>
<div class="form-group">
<label for="exampleFormControlFile2">Snaps of Project</label>
<input class="form-control-file" name="images" type="file" id="exampleFormControlFile2" multiple>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" >Update</button>
</div>
</form>
</div>
<div class="card-body">
<div class="table-responsive">
</div>
</div>
</div>
My router.js file code is here and two lines of code in here one is get request and other is for post
router.get('/admin/portfolio/:id/cover_edit',serverController.isAuthenticated,serverController.portfolio_edit_cover)
router.post('/admin/portfolio/:id/cover_edit',serverController.isAuthenticated,upload.fields([{name: "cover", maxCount: 1},{name: "images", maxCount: 12}]),serverController.portfolio_edit_cover_post)
here is my backend controller and code for images updating
exports.portfolio_edit_cover = async function(req,res){
res.render('server/edit_cover',{
work : await workCollection.findOne({_id: objectId(req.params.id)})
})
}
exports.portfolio_edit_cover_post = function(req,res){
let uploadImages = new Work(req.body, req.files, req.params.id)
console.log(req.files)
uploadImages.up().then((result)=>{
res.redirect('/admin/portfolio',{
work : result
})
}).catch(()=>{
res.send('404')
})
}
and lastly this all about my model codes. in the following code is just for how update my data onto databae
Work.prototype.up = function(){
return new Promise(async(resolve,reject)=>{
await workCollection.updateOne({_id : objectId(this.id)}, {$set :
{
images : this.images
}
}
)
resolve()
})
}
module.exports = Work
in edit_cover.js file it has path like
action="/admin/portfolio/<%=work._id%>**/edit_cover**" and router.js it's like
router.get('/admin/portfolio/:id**/cover_edit**',serverController.isAuthenticated,serverController.portfolio_edit_cover)
router.post('/admin/portfolio/:id**/cover_edit**',serverController.isAuthenticated,upload.fields([{name: "cover", maxCount: 1},{name: "images", maxCount: 12}]),serverController.portfolio_edit_cover_post)
both of these need to have the same path; everything is fine but at last segment, it should be /cover_edit

Image upload in a form

I have an angular form with text fields and want to add am option for uploading image in the same form. I'm stuck with the latter.
The angular form contains html tag to upload the file. Once the file is uploaded the uploaded file's name is displayed in the input field.
<!--blog.html-->
<!--form to create new blog-->
<form #blogForm="ngForm" (ngSubmit)="Save(blogForm);">
<div class="form-group">
<label>Blog Title</label>
<input type="text" class="form-control input-text" maxlength="45" name="title" ngModel #blogtitle="ngModel" required placeholder="Blog Title">
<span class="required" *ngIf="blogtitle.errors?.required && (blogtitle.dirty||blogtitle.touched||blogtitle.untouched)">*required</span>
</div>
<div class="form-group">
<label>Blog </label>
<textarea class="textarea form-control" maxlength="150" name="summary" [(ngModel)]="summary">
Blog</textarea>
</div>
<div class="form-group">
<label>Blog Desc</label>
<textarea class="textarea form-control" name="description" ngModel #blogdescription="ngModel" required>Blog Description</textarea>
<span class="required" *ngIf="blogdescription.errors?.required && (blogdescription.dirty||blogdescription.touched||blogdescription.untouched)">*required</span>
</div>
<div class="form-group">
<label>HashTags</label>
<input type="text" class="form-control input-text" name="hashtag" [(ngModel)]="hashtag" placeholder="hashtags">
</div>
<div class="form-group">
<div class="custom-file">
<!--file upload -->
<input type="file" class="custom-file-input form-control-lg" name="file" id="customFile"
value="imageFile.name" (change)="handleImageFileInput($event)">
<input type="text" readonly="true" [value]="imageFile" class="custom-file-label" >
<button type="button" (click)="upload()">Upload</button>
</div>
</div>
<input type="button" class="btn-default" (click)="Save(blogForm)" value="Create">
</form>
//blog.ts
//function to create new blog
Save(blogForm: any) {
if (blogForm.valid === true) {
blogForm = blogForm.value;
blogForm.userId = this.user_id;
blogForm.author = this.display_name;
window.confirm('Want to Publish?');
this.blogservice.Save(blogForm).subscribe(response => {
window.alert('Blog published successfully');
this.router.navigate(['/dashboard']);
});
}
}
//function to display selected image in the input field
handleImageFileInput(event) {
const img1 =event.target.files[0];
const img =event.target.files[0].name;
const fileList: FileList = event.target.files;
const fileCount = fileList.length;
if (fileCount > 0) {
const file = fileList.item(0);
console.log(` image file: ${file.name}`);
console.log(img1);
if(img == undefined) {
this.imageFile = 'Upload an image';
} else {
this.imageFile = img;
}
}
}
Need to save file along with form submission
Here's a JavaScript script which will read your data from input and display it :
<input type='file' accept='image/*' onchange='openFile(event)'><br>
<img id='output'>
<script>
var openFile = function(event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){
var dataURL = reader.result;
var output = document.getElementById('output');
output.src = dataURL;
};
reader.readAsDataURL(input.files[0]);
};
</script>
It come from the documentation of FileReader.
With that you should be able to store the input wherever you want and store the path inside of your MongoDB collection.
Else if you want to use Angular plugins, here's one that could be useful for you :
angular-file-upload

How to use endpoint inside another endpoint in express node js

I am just stuck in a problem and also don't know whether the question is correct or not.
I am working on a node express js, using ejs templating engine.
I have created certain endpoints (say login(POST) endpoint) which are generic endpoints.
Now I want to create a new application which uses should use these endpoints and render the view according to the result of these endpoints.
Lets say I have a generic endpoint for login
router.post('/user/login', function (req, res) {
var userLoginId = req.body.username;
var currentPassword = req.body.password;
UserLogin.findOne({userLoginId: userLoginId, currentPassword: currentPassword}, function (err, usr) {
if (err) {
console.log('Error in User login');
console.log(err);
res.status(500).type('application/problem+json').send();
} else {
if (!usr) {
console.log('Please enter valid username and password');
res.status(404).type('application/problem+json').send();
} else {
res.type('application/json').status(200).send(usr);
}
}
});
});
And I am using this login.ejs file
<div class="container panel panel-primary">
<h2 class="h2 text-center">Login</h2>
<form class="form-horizontal" name="loginForm" method="post" action="/user/login">
<div class="form-group">
<label for="username" class="col-sm-offset-2 col-sm-2 control-label">UserName</label>
<div class="col-sm-4">
<input type="text" name="username" class="form-control" id="username" placeholder="username">
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-offset-2 col-sm-2 control-label">Password</label>
<div class="col-sm-4">
<input type="password" name="password" class="form-control" id="password" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-4">
<button type="submit" class="btn btn-primary btn-">Sign in</button>
</div>
</div>
</form>
</div>
Now I want to use this code and want to login to the system.
If the user login is successful I want to redirect to dashboard page else same login page.
On executing this code the api returns me the JSON response and from this response how can I decide to whether to redirect on dashboard page or login page.
Please help if more input is needed please put a comment.
I would instead of doing a form request to login, do a AJAX request to login and then use something like jQuery to do the AJAX request and response. Alternatively you could do something like the following.
router.get('/login', function(req, res){
res.render('login', {});
})
router.post('/login', function (req, res) {
var userLoginId = req.body.username;
var currentPassword = req.body.password;
UserLogin.findOne({userLoginId: userLoginId, currentPassword: currentPassword}, function (err, usr) {
if (err) {
console.log('Error in User login');
console.log(err);
// res.status(500).type('application/problem+json').send();
res.status(500).render('login', { error : 'Error finding password'})
} else {
if (!usr) {
console.log('Please enter valid username and password');
// res.status(404).type('application/problem+json').send();
res.status(404).render('login', { error : 'Please enter valid username and password'})
} else {
res.redirect('/dashboard');
}
}
});
});
For the login.ejs file i would conditionally render the error.
<!DOCTYPE html>
<html>
<body>
<div class="container panel panel-primary">
<h2 class="h2 text-center">Login</h2>
<%if (locals.error) { %>
<%= error %>
<% } %>
<form class="form-horizontal" name="loginForm" method="post" action="/user/login">
<div class="form-group">
<label for="username" class="col-sm-offset-2 col-sm-2 control-label">UserName</label>
<div class="col-sm-4">
<input type="text" name="username" class="form-control" id="username" placeholder="username">
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-offset-2 col-sm-2 control-label">Password</label>
<div class="col-sm-4">
<input type="password" name="password" class="form-control" id="password" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-4">
<button type="submit" class="btn btn-primary btn-">Sign in</button>
</div>
</div>
</form>
</div>
</body>
</html>

Resources