How to upload a picture using feathers.js and multer? - javascript

I'm working with feathers.js on the back-end and React on the front end and I need to implement a way to upload a picture. I'm using multer to handle the upload (and I've tried using busboy as well), but I can't seem to get the actual picture uploaded, or at least access it on req.file.
On the client side I have:
<form method="post" enctype="multipart/form-data" action="/picture/upload">
<input type="file" name="avatar" />
<input type="submit" value="Submit" />
</form>
In /src/middleware/index.js I have:
'use strict';
const uploadPicture = require('./uploadPicture');
const handler = require('feathers-errors/handler');
const notFound = require('./not-found-handler');
const logger = require('./logger');
var multer = require('multer');
const upload = multer({ dest: '../../client/img'});
module.exports = function() {
// Add your custom middleware here. Remember, that
// just like Express the order matters, so error
// handling middleware should go last.
const app = this;
app.post('/picture/upload', upload.single('avatar'), uploadPicture(app));
app.use(notFound());
app.use(logger(app));
app.use(handler());
};
This is src/middleware/uploadPicture.js:
'use strict';
module.exports = function(app) {
return function(req, res, next) {
console.log('req.file', req.file);
console.log('req.body', req.body);
};
};
req.file is always undefined, and req.body does contain the name of the image I uploaded.
I have tried using mutler and busboy on a basic Express project for testing, and it works perfectly, so that makes me think that maybe it has something to do with the middleware feathers.js uses and probably it changes some header or something, so multer can't append the file to the request object.
This is the order in which middleware is defined in src/app.js, which is where the server instance is run:
app.use(compress())
.options('*', cors())
.use(cors())
.use('/', serveStatic( app.get('client') ))
.use(bodyParser.json())
.use(bodyParser.urlencoded({ extended: true }))
.configure(hooks())
.configure(rest())
.configure(services)
.configure(middleware);
Any thoughts on how to handle the image upload in this scenario?

I am using react, so enctype is not a supported HTML attribute. I should have used encType in my form. That fixed the problem.

Related

Formidable Not Uploading When Using Multer

I'm trying to create a page that lets you upload a file to a folder and say what a number is in an input. I'm new to the multer library(which is what I'm using right now) and I usually use body-parser. Here is my app.js and my upload.ejs, I'll explain what my problem is below it.
app.js
const express = require('express');
const http = require('http');
const formidable = require('formidable');
const fs = require('fs');
const ejs = require('ejs')
const path = require('path');
const multer = require('multer');
const upload = multer({ dest: 'upload/'});
const type = upload.any('gradeNumber');
const app = express();
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
app.get('/fileupload', function (req, res) {
res.render("upload")
});
app.post('/fileupload',type, function (req, res) {
const form = new formidable.IncomingForm();
console.log(req.body.gradeNumber);
form.parse(req, function (err, fields, files) {
const oldpath = files.filetoupload.path;
const newpath = 'C:/Users/Shubh Computer/Desktop/VSCode/Grades/1/' + files.filetoupload.name;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
res.end();
});
});
});
app.listen(3000);
upload.ejs
<!DOCTYPE html>
<html>
<head>
<title>Upload File</title>
</head>
<body>
<form action="fileupload" method="post" enctype="multipart/form-data">
<input type="file" name="filetoupload"><br>
<label>What Grade</label><input type="text" name="gradeNumber"><br>
<input type="submit">
</form>
</body>
</html>
Before I started to use multer, I was uploading the file to a specific folder without error, but would get the input value as undefined. After I added the app.post(/fileupload,type,function(req,res){}) I can log the input but can't upload the file(If I take out the type I can still upload the file). I was also wondering what the const upload = multer({ dest: 'upload/'}); does because I have a folder(not created by me) that holds weird files that Visual Studio can't support(Notepad can't support it either). I have a strong feeling it's something to do with that line, but like I said, I'm new to Multer. If somebody could come up with a solution that can upload and log the value that would be great!(I have spent a long time writing this I'm trying to get my reputation up so my votes can display to the public; I wouldn't mind if you can upvote my post!!)
Maybe start the URL with a / so that the intended URL is returned instead of its parent or child.
It might solve the folder issue.
and about the "type" middelware, I haven't seen yet any example of upload.any() with passing parameters.
I'm new to Multer too, but I think it might solve the problems
You dont need formidable if you use multer middleware. Multer will parse the input form and populate it in req.body as usual.The file data will be populated in req.files.

Disable bodyparser for file uploads - Nodejs

This question is very similar to How to disable Express BodyParser for file uploads (Node.js). The answer they have provided is for Express3 and I have tried the solution with the updated Express 4 and it does not seem to work.
I'm using Node.js + Express to build a web application. I am using another library,BodyParser,to parse post parameters. However, I would like to have more granular access to multipart form-data POSTS as they come - I need to pipe the input stream to another server, and want to avoid downloading the whole file first.
All file uploads are parsed automatically and uploaded and available using "request.files" before they ever get to any of my functions.
Is there a way for me to disable the BodyParser for multipart formdata posts without disabling it for everything else?
This is my app.js file. In here I am defining an authentication route which shouldn't except any files just a token (POST parameter). I am also defining another route called upload. This route accepts a file and also POST parametes (form-data). This route only gets called if the authentication route allows it. So in the authetnication route I don't want form-data to be allowed, but in the upload route I do. So when I get a request to uplaod something it will go through the auth route and then the upload route. Due to this I need to allow the auth route to allow files (form-data) which I do not want. So I want bodyparser to work in the auth route while I use mutler (another library) in my upload path to parse my upload files. In my real application though of course I have many more routes and would like to code it as cleanly as I can with the least amount of redundancy.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({
extended: true
}));
var route_auth = require('./routes/auth');
app.use('/api/post/*', route_auth);
var route_upload = require('./routes/post/upload');
app.use('/api/post/upload', route_upload );
app.listen(3000, function() {
console.log('Server listening on port 3000!')
});
My auth route looks something like this:
router.post("/", function(req, res, next) {
if(everythingiscool){
return next()
}
next(err);
});
My upload route looks like this:
var express = require('express');
var router = express.Router();
var multer = require('multer')
var upload = multer({ dest: 'uploads/' });
router.post("/", upload.single('avatar'), function(req, res, next) {
//work with req.file
});
Wrap the bodyParse middleware in a function that checks if the request body's Content-Type is multipart or not:
var isMultipart = /^multipart\//i;
var bodyParser = require('body-parser');
var urlencodedMiddleware = bodyParser.urlencoded({ extended: true });
app.use(function (req, res, next) {
var type = req.get('Content-Type');
if (isMultipart.test(type)) return next();
return urlencodedMiddleware(req, res, next);
});
Instead of disabling, why not enable the middleware on the routes/routers where you need it?
For individual routes, you can just add it as another argument before your actual route handler, for example:
app.post('/upload', bodyParser, (req, res) => {
// route logic here
});

angular file upload with nervgh/angular-file-upload

I'm doing a file upload from angular implementing the https://github.com/nervgh/angular-file-upload plugin, my setup it's like this :
var vm = this,
apiUrl = appConfig.getItem('BASE_API_URL'), // base url;
vm.uploader = $scope.uploader = new FileUploader({
url: apiUrl + '/invoices/upload',
headers: {
"Content-Type": undefined
}
});
// this function is triggered by a button outside
vm.uploadAll = function () {
vm.uploader.uploadAll();
};
on the html I have
<input id="uploadFileButton"
type="file"
nv-file-select
uploader="FUCtrl.uploader"
multiple
style="display: none;"/>
// the display none is due to that this input is click triggered
// by an outside button
the thing is that for start on the client side the post request I see this
an image is uploaded (in theory), but the Content-Type is undefined, a missing enctype, and on the other hand on the server side I have this
var express = require('express'),
multer = require('multer'),
cors = require('cors');
var app = express();
app.use(cors());
app.get('*', function(){});
app.use(multer({dest:'./uploads/'}).single('photo'));
app.post('/upload', function(req, res){
console.log('hit');
console.log(req.body); // form fields
console.log(req.files); // form files
res.status(204).end();
});
app.listen(3000);
but when I recieve the post I see on the console
console.log(req.body); // {}
console.log(req.files); // undefined
and I can't get any data from the pdf's upload
what am I missing ?
hey man i don't know about that plugin. but i am using this https://github.com/danialfarid/ng-file-upload plugin and i find very helpful. this is the easiest way to upload a file.
Upload with form submit and validations:
http://jsfiddle.net/danialfarid/maqbzv15/1118/
Upload multiple files one by one on file select:
http://jsfiddle.net/danialfarid/2vq88rfs/136/
Upload multiple files in one request on file select (html5 only):
http://jsfiddle.net/danialfarid/huhjo9jm/5/
Upload single file on file select:
http://jsfiddle.net/danialfarid/0mz6ff9o/135/
Drop and upload with $watch:
http://jsfiddle.net/danialfarid/s8kc7wg0/400/
Image Crop and Upload
http://jsfiddle.net/danialfarid/xxo3sk41/590/
Finally I've discoverded the solution, on the HTML
nv-file-select
should be
nv-file-select=""
and remove
headers: {
"Content-Type": undefined
}
from the uploader configuration, apparently those things didn't set the content-type and his boundary well, so I toke them out and goy it to work

Storing a file into MongoDB using Multer in Mongoose

I have gotten this far to accept a file in my HTML form and post in with angular via an $http.post using the ng-file-upload module. Now I want to accept this file in Mongoose and store it into my NoSQL MongoDB hosted on MongoLab.
I have read about this module called Multer and followed the basic documentation, but it only me as far. Before I explain the beginning of the problem let me post my Code:
My HTML form:
<form name="upForm">
<fieldset>
<legend>Upload files here</legend>
<label>Insert File Here:</label>
<input type="file" ngf-select ng-model="exFile" name="file" ngf-accept="'.cs'" required>
<i ng-show="upForm.file.$error.required">*required</i>
<div class="alert" ng-show="file.$error === 'pattern'">
file type is not accepted
</div>
<br />
<button ng-disabled="!upForm.$valid" ng-click="uploadExercise(exFile)" class="btn btn-default">Submit</button>
<span class="progress" ng-show="picFile.progress >= 0">
<div style="width:{{exFile.progress}}%" ng-bind="picFile.progress + '%'"></div>
</span>
<span ng-show="picFile.result">Upload Successful</span>
</fieldset>
</form>
My Angular Code:
$scope.uploadExercise = function (file) {
console.log(file);
var fd = new FormData();
fd.append('file', file);
$http.post(url+"/Upload", fd,{
transformRequest: angular.identity,
header:{'Content-Type': undefined},
enctype:'multipart/form-data'
}).success(function () { }).error(function () { });
console.log(fd);
};
console logs return the correct file objects.
Mongoose so far:
var mongoose = require("mongoose");
var express = require("express");
var multer = require('multer');
var upload = multer({ dest: 'Uploads/' });
var bodyparser = require("body-parser");
var app = express();
mongoose.connect("connection-string");
app.use(bodyparser.json());
app.post('/Upload', upload.single('solution') ,function (req, res, next) {
console.log(req.file);
});
This console.log keeps returning undefined. So something, somewhere went terribly wrong. Please help me out!
I want to receive this file in my Mongoose and store it into the MongoDB, I have never done this before and can't seem to find any decent documentation for Multer or any decent explanation for storing files that is relevant for my case. What am I doing wrong? What should I be doing instead?
Man you are running on the wrong path. I have already explained in your previous question request that Multer is used to save files in file disk system and not to your database directly. For that you must use GRIDFS.
Coming to your current question.
app.post('/Upload', upload.single('solution') ,function (req, res, next) {
console.log(req.file);
});
Here the upload.single('solution') - calls a function Upload and the file name passed is solution but it is obvious enough that it isn't available here.
use this type of format - documentation of Multer
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
The Storage Part there is used to give path to where your file must be saved and the file name section is used to make changes to the file name that you would like to have.
Please read the documentation because that'll help. When we use third party modules we must acknowledge the information they have already given so that we can use their work easily.
Let me make it easier for you. Here is ready made code that works.
Multer throwing weird error while uploading file via ng-file upload
Go check that thread. The question was raised by me - the problem there was I was sending files in array format, as in multiple files at once. If you are not doing that just change ng-file-upload segment to use the single upload demo example and on server side nodejs code replace .array with .singleand things will work the way you want them to work - given that you want to use file disk system to store files.
I repeat that this method wont help you to save the file in mongodb directly.
Let me know if you need any further clarification.
After some research I found the answer to my problem. I'm able to store files into my MongoDB now. For those who are interested I'm posting my Mongoose code below with a small description.
Start by installing packages required for this operation. I used the following ones
npm install --save formidable
npm install --save gridfs
npm install --save fs
these are the packages I used ( Formidable, gridfs and fs).
this is the fully working mongoose code for me relevant to this particular form and request.
var mongoose = require("mongoose");
var express = require("express");
var formidable = require("formidable");
var fs = require("fs");
var grid = require("gridfs-stream");
var bodyparser = require("body-parser");
var app = express();
mongoose.connect("**fill in your connection-url here**");
var conn = mongoose.connection;
app.use(bodyparser.json());
app.post('/Upload', function (req, res) {
var form = new formidable.IncomingForm();
form.uploadDir = __dirname+"/Uploads";
form.keepExtensions = true;
form.parse(req, function (err, fields, files) {
if (!err) {
console.log('Files Uploaded: ' + files.file)
grid.mongo = mongoose.mongo;
var gfs = grid(conn.db);
var writestream = gfs.createWriteStream({
filename: files.file.name
});
fs.createReadStream(files.file.path).pipe(writestream);
}
});
form.on('end', function () {
res.send('Completed ... go check fs.files & fs.chunks in mongodb');
});
});
this worked for me! I now went to look in my mongoDB hosted on MongoLab and see that fs.chunks and fs.files collections were created and fs.files contains the correct data.
So for those who have this problem, this is a solution and the documentation on http://excellencenodejsblog.com/gridfs-using-mongoose-nodejs/ helped me out a lot. Now that I have this working, I also want to download the file FROM mongoDB onto a chosen directory on my pc, is there anyone who can give me the answer to that? or is that just as simple as to create a readstream to a file-system?

Where does the Node.js form data end up?

I created this small example to find out where the form data end up, but I can't figure out where.
I would expect to find some data in req.body, but as you see if you run the example, if comes out empty :/
var connect = require('connect');
var bodyParser = require('body-parser');
var app = connect();
app.use(bodyParser.urlencoded({'extended': false}));
app.use(function(req, res){
console.log(req);
res.end("<html><form method='post'enctype='multipart/form-data'><input type='text' name='file_caption' /><input type='file' name='file_file' /><input type='submit' /></form></html>");
});
app.listen(3000);
The thing is, if I use connect-busboy to access the input and file I find them. But why aren't they visible in the request object?
body-parser doesn't provide any middleware to handle multipart requests. For that you need something like multer (multipart only), busboy/connect-busboy, or multiparty.

Categories

Resources