Uploading files in forlder with react/express/multer - node.js

I'm trying to upload big files with express and multer.
The call back request send 200 but I don't know really how upload my files in a specific folder. I tried two way without success.
The first one:
app.post('/upload', (req, res, next) => {
let myFile = req.files;
let i = 0;
for (i; i < myFile.length; i++) {
let filemName = MyFile[i].name
myFile[i].mv(`${__dirname}/uploads/${fileName}`, function(err) {
if (err) {
return res.status(500).send(err);
}
res.json({file: `uploads/${fileName}`});
});
}
})
this code return 500.
The second way (tried only with one file):
app.post ('/uploader', (req, res, next) => {
var file = req.files.file;
// file.mv(`${__dirname}/uploads/${file.name}`), function(err) {
// if (err) {
// return res.status(500).send(err);
// }
// }
console.log('file');
console.log(file[0]);
fs.rename(req.file[0], '~/dev/file-upload/backend/uploads' + file[0].name, function (err) {
if (err) throw err;
console.log('Move complete');
})
this code doesn't return any error but doesn't put the file in the folder.
And finaly my client side code:
handleUploadFile(event) {
event.preventDefault();
const data = new FormData();
let c = this.uploadInput.files.length;
console.log("c = ", c);
console.log("mydata :", this.uploadInput.files);
for (var i = 0; i < c; i++){
data.append('filename', this.uploadInput.files[i].name);
data.append('file', this.uploadInput.files[i]);
}
var options = {
method: 'post',
body: data,
}
fetch(apiBaseUrl + '/uploader', options).then((response) => {
console.log('res', response);
}).catch(function (err) {
console.error("!!! error :", err);
})
}
render() {
return (
<form onSubmit={this.handleUploadFile}>
<div>
<input ref={(ref) => { this.uploadInput = ref; }} type="file" multiple="multiple" />
</div>
<br />
<div>
<button>Upload</button>
</div>
</form>
);
}
Thanks by advance for your help :)

I fixed my issue.
body-parser doesn’t support parsing the body of a multipart/form-data request.
The following link gave me all the answers needed.

Related

hashing a streaming file before uploading to S3

I am trying to stream a file to S3 without storing the file to disk/ssd. I would like to have part of the hash of the file as a part of the filename when uploading to S3.
EDIT_v1:
Been trying to follow this post using busboy as the parser: Calculate a file hash and save the file. I took an example from the busboy docs and adabpted it with an answer from the post:
const server = http.createServer();
server.on('request', async (req, res) => {
if (req.method === 'POST') {
const bb = busboy({ headers: req.headers });
bb.on('file', (name, file, info) => {
const { filename, encoding, mimeType } = info;
console.log(
`File [${name}]: filename: %j, encoding: %j, mimeType: %j`,
filename,
encoding,
mimeType
);
const fileHashSource = new PassThrough();
const writeSource = new PassThrough();
file.pipe(fileHashSource);
file.pipe(writeSource);
fileHashSource.resume();
writeSource.resume();
createFileHash(fileHashSource, (err, hash) => {
if (err) {
console.log('err', err)
return res.end('some err');
}
const writeStream = fs.createWriteStream(`test_${hash.slice(0, 8)}.png`);
writeStream.on('error', function(err) {
console.log('write error', err);
return res.end('write error')
});
writeStream.on('finish', function() {
console.log('write finished')
return res.end('done')
});
writeSource.pipe(writeStream);
});
});
bb.on('field', (name, val, info) => {
console.log(`Field [${name}]: value: %j`, val);
});
bb.on('close', () => {
console.log('Done parsing form!');
req.unpipe(bb);
res.writeHead(201, { Connection: 'close' });
res.end('done!');
});
req.pipe(bb);
} else if (req.method === 'GET') {
res.writeHead(200, { Connection: 'close' });
res.end(`
<body style="background-color: black">
<form enctype="multipart/form-data" method="post">
<label>file name
<input type="text" name="textfield" />
</label><br />
<label>single file
<input type="file" name="filefield" />
</label><br />
<br />
<button type="submit">Upload</button>
</form>
</body>
`);
}
})
server.listen(3000, () => {
console.info(`NodeJS process: ${process.pid}`)
console.info(`Listening on port: 3000`)
});
function createFileHash(readStream, next) {
const hash = crypto.createHash('sha1');
hash.setEncoding('hex');
hash.on('error', function(err) {
console.log('hash error')
return next(err);
});
hash.on('finish', function(data) {
console.log('hash finished');
return next(null, hash.read());
});
readStream.pipe(hash);
}
EDIT_v2:
see first answer below for a solution
I put the task flow in a pipeline, implemented late piping with PassThrough, and finally used a function that returns an async generator that uploads to S3
const { fileStream, mimeType } = createFromBusBoy();
const s3Source = new PassThrough();
filestream.on('data', chunk => {
s3Source.write(chunk);
});
filestream.on('end', () => {
s3Source.end();
});
const hash = createHash('sha256');
hash.setEncoding('hex');
try {
await pipeline(
filestream,
hash,
uploadImage(s3Source, mimeType),
);
} catch (err) {
console.log(err)
throw err;
}
function uploadImage(fileStream, mimeType) {
return async function* (source, signal) {
let hash;
for await (const chunk of source) {
hash = chunk;
}
yield await uploadToS3(filestream, hash, mimeType);
};
}

Why the complete JSON is not being sent to mongodb using mongoose?

I have a functionality on a webpage, that uploads an excel file to Node and parse it to JSON, then pass the data to mongodb. It only sends one document to mongodb, each document is organized inside of an array and iterate through each one using a for loop, so the only one that is being sent is the first document. I tried also to use the model.create(docs) function which sends each document to the database but is the same issue. This is the code(the model.create(docs) is inside the //////////////):
app.post('/upload', function(req, res){
var exceltojson;
upload(req, res, function(err){
if (err) {
res.json({error_code:1,err_desc:err})
return;
}
if(!req.file){
res.json({error_code:1, err_desc:"No file passed"});
return;
}
if(req.file.originalname.split('.')[req.file.originalname.split('.').length-1] === 'xlsx'){
exceltojson = xlsxtojson;
} else {
exceltojson = xlstojson;
}
try {
exceltojson({
input: req.file.path,
output: "./outPutJSON/output.json",
lowerCaseHeaders: true
}, function(err, result){
if(err){
return res.json({error_code:1, err_desc:err, data: null});
}
res.json({datos:"Los datos fueron agregados exitosamente"});
//res.json({error_code:0, err_desc:null, data: result});
let resultDos = fs.readFile("./outPutJSON/output.json", 'utf8', (err, fileContents) => {
if (err) {
console.error(err)
return;
}
try {
const data = JSON.parse(fileContents)
console.log(data.length);
//////////////////////////////////////////////////
model.create(data, function (err) {
if(err){
console.log(err);
}
});
///////////////////////////////////////////////////
//for(var cantidad = 0; cantidad < data.length;cantidad++{
//let documento = data[cantidad];
//let mod = new model(documento);
//console.log(documento);
// mod.save(function(err){
// if(err){
// console.log(err);
// }
// });
//}
//////////////////////////////////////////////////////
} catch(err) {
console.error(err);
}
})
console.log(resultDos);
});
var fs = require('fs');
try {
fs.unlinkSync(req.file.path)
}catch(e){
}
} catch (e) {
res.json({error_code:1, err_desc:"Corrupted excel file"});
}
});
});
This is the JSON file:
Only this one is sent -->{"nombre":"Wilson Junior Toribio","cedula":"4022589632","direccion":"Calle 7 #33 Buenos Aires"},
{"nombre":"Jose Luis Toribio","cedula":"4023495023","direccion":"Calle 11 # 69 Buenos Aires"},
{"nombre":"Joel de Jesus Toribio","cedula":"4023548902","direccion":"Calle 1 # 3 Buenos Aires"},
{"nombre":"Corazon Roa","cedula":"4026984452","direccion":"Calle 3 # 19 Buenos Aires"}
I even output each document to verify if the documents are being store in the variable, this is the output:
The problem has been solve, I had to edit the callback to be synchronous using async and await and also use let to declare the variable cantidad inside the for:
let resultDos = fs.readFile("./outPutJSON/output.json", 'utf8', -> async (err, fileContents) => {
if (err) {
console.error(err)
return;
}
try {
let data = JSON.parse(fileContents)
console.log(data.length);
console.log(data);
// model.create(data, function (err) {
// if(err){
// console.log(err);
// }
// });
for(let cantidad = 0; cantidad < data.length; cantidad++){
var documento = data[cantidad];
var mod = new model(documento);
console.log(documento);
-> await mod.save(documento);
// model.create(documento).save();
}

Rendering variable to ejs template?

My ejs side (webpage) is updating the wrong variables every time I refresh the page,but each variable has a different name. I cannot figure out what is wrong.
My index.js is receiving messages from an esp8266 using MQTT, and then I render that to the ejs.
index.js
var topicTemp = "FromESPtoWeb/temp";
var topicMessagemoisture = "FromESPtoWeb/moisture";
var content = { doorMsg: "Door Closed" ,
windowMsg: "Window Closed",
tempMsg : "",
moistureMsg : "" ,
motionMsg: "Motion" };
client.on('connect', function () {
//Subscribe to topic "FromESPtoWeb/temp"
client.subscribe(topicTemp, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
client.on('message', function (topicTemp, temp) {
console.log(temp.toString());
content.tempMsg = temp.toString();
});
})
//Subscribe to topic "FromESPtoWeb/moisture"
client.subscribe(topicMessagemoisture, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
client.on('message', function (topicMoisture, moisture) {
console.log("new message on " + topicMoisture + " - " +
moisture.toString());
content.moistureMsg = moisture.toString();
});
})
})
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { content : content } );
});
index.ejs
<h4> <%= content.moistureMsg %> </h4>
<h4> <%= content.motionMsg %> </h4>
<h4> <%= content.windowMsg %> </h4>
<h4> <%= content.doorMsg %> </h4>
content.moistureMsg sometimes showing what is suppose to be to content.windowMsg , or content.doorMsg is showing the value that is suppose to be to content.motionMsg. A complete mess...
Use the request object!.
router.get('/', function(req, res) {
res.render('index', { content : req.content } );
});
My understanding was very wrong about client.on and subscribe. I rebuilt the whole code, and now it is working.
var topicTemp = "FromESPtoWeb/temp";
var topicDoor = "FromESPtoWeb/door";
var topicWindow = "FromESPtoWeb/window";
var topicMoisture = "FromESPtoWeb/moisture";
var topicMotion = "FromESPtoWeb/motion";
var content = { doorMsg: "Door Closed" , windowMsg: "Window Closed", tempMsg:"", moistureMsg:"", motionMsg: ""};
client.on('connect', function () {
client.on('message', function (topic, message) {
if(topic === topicTemp) {
temp(message);
}
if(topic === topicDoor) {
door(message);
}
if(topic === topicWindow) {
window(message);
}
if(topic === topicMoisture) {
moisture(message);
}
if(topic === topicMotion) {
motion(message);
}
});
client.subscribe(topicTemp, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
});
client.subscribe(topicDoor, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
});
client.subscribe(topicWindow, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
});
client.subscribe(topicMoisture, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
});
client.subscribe(topicMotion, function (err) {
if (err) {
alert("something went wrong on subscribe to message");
}
});
});
var temp = (message) => {
console.log(message.toString());
content.tempMsg = message.toString();
}
var door = (message) => {
if (message == "Door Open") {
console.log("Door open");
content.doorMsg = message;
}else if (message == "Door Closed") {
console.log("Door closed");
content.doorMsg = message;
}
}
var window = (message) => {
if (message == "Window Open") {
console.log("window open");
content.windowMsg = message;
}else if (message == "Window Closed") {
console.log("window closed");
content.windowMsg = message;
}
}
var moisture = (message) => {
console.log(message.toString());
content.moistureMsg = message.toString();
}
var motion = (message) => {
console.log(message.toString());
content.motionMsg = message.toString();
}
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { content : content } );
});

Download file from server (Nodejs Express > React)

how can i send a file(docx) to a user ?
this is my server code :
app.get('/api/topic/file/:id', function (req, res, next) {
Topic.findByIdAndUpdate(req.params.id)
.exec()
.then((topic) => {
let filepath = topic.news_file[0]
console.log('filepath', filepath)
res.download(filepath, topic.name + '.docx', function (err) {
if (err) {
console.log('api get file err ', err);
} else {
// decrement a download credit, etc.
}
});
}).catch((err) => console.log('error', err));
})
this does not trigger a download on the browser.
i am using react as front-end.
on the client i have a button triggering this upon click :
handleDownload() {
if (this.state.lastClicked) {
fetch("/api/topic/file/" + this.state.lastClicked._id)
.then(results => {
console.log('results', results)
return results;
})
} else {
//somthings...
}
}
Found a solution using downloadjs..
var download = require("downloadjs")
async handleDownload() {
const res = await fetch("/api/topic/file/" + this.state.lastClicked._id);
const blob = res.blob();
download(blob, this.state.lastClicked.name + '.docx');
}

Uploading files with Angular to Multer

I have a problem with uploading to my backend with Angular.
this is my component.html
<input type="file" (change)="fileChange($event)" placeholder="Upload file">
This is my component.ts
fileChange(event) {
let fileList: FileList = event.target.files;
if(fileList.length > 0) {
let file: File = fileList[0];
let formData: FormData = new FormData();
formData.append('file', file, file.name);
console.log(formData);
let headers = new Headers();
headers.append('Accept', 'application/json');
const token = localStorage.getItem('token') ? '?token=' + localStorage.getItem('token') : '';
this.http.post('http://localhost:3000/stretch/1' + token, formData, { headers: headers })
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log(data),
error => console.log(error)
);
}
}
This is my backend with NodeJS/express + Multer
var upload = multer({ dest: 'uploads/' }).single('file');
router.post('/1', upload, function (req, res, next) {
var decoded = jwt.decode(req.query.token);
User.findById(decoded.user._id, function (err, user) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err,
});
}
if (!req.file){
return res.status(500).json({
title: 'An error occurred',
error: "No invoice document recieved"
});
}
});
});
And this is what I get on my console:
Screenshot of console
I am not sure what the problem is or what I am missing I feel like I tried almost anything.
What confuses me though is that when I console.log the formData, it look empty even though I just selected a file. Is that normal?
Your help is much appreciated.
import { Component,ElementRef, OnInit } from '#angular/core';
import { Http, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
import "rxjs/add/operator/do";
export class HomeComponent implements OnInit {
constructor(private http: Http,private el: ElementRef) {
}
upload() {
let inputEl: HTMLInputElement = this.el.nativeElement.querySelector('#photo');
let fileCount: number = inputEl.files.length;
let formData = new FormData();
if (fileCount > 0) { // a file was selected
for (let i = 0; i < fileCount; i++) {
formData.append('file', inputEl.files.item(i));
}
let headers = new Headers();
headers.append('Accept', 'application/json');
const token = localStorage.getItem('token') ? '?token=' + localStorage.getItem('token') : '';
this.http.post('http://localhost:3000/stretch/1' + token, formData, { headers: headers }).map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log(data),
error => console.log(error)
);
}
}
}
// html code
<input id="photo" type="file" />
<button type="button" class="btn btn-success btn-s" (click)="upload()">Upload</button>
my node code
var multer = require('multer'); //FOR FILE UPLOAD
var storage = multer.diskStorage({ //multers disk storage settings
destination: function (req, file, cb) {
cb(null, './public/uploads'); //image storage path
},
filename: function (req, file, cb) {
var datetimestamp = Date.now();
cb(null, file.originalname);
}
});
var upload = multer({ //multer settings
storage: storage
}).single('file');
router.post('/1', upload, function (req, res, next) {
var decoded = jwt.decode(req.query.token);
var path = '';
User.findById(decoded.user._id, function (err, user) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err,
});
}
if (req.file){
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
return res.status(422).send("an Error occured");
}
// No error occured.
path = req.file.path;
return res.status(200).send(path);
});
}
});
});

Resources