I'm (very) new to angular MEAN and I'm trying to upload a file (pdf, specifically) and save it to server. I know it's probably a stupid question, but I cannot find any example on the server on how to actually save the uploaded file to the server's storage
I'm using ng-file-upload directive from https://github.com/danialfarid/ng-file-upload, Express for server, and ofc, AngularJS for the file upload.
POST UPDATED!! See below
More info: I'm using Yeoman's full mean stack generator for this project
UPDATE:
I've tried using multer (https://github.com/expressjs/multer) to save the uploaded file to server. I got this error when trying to upload the file (it returns 500 error)
Error: Unexpected field
at makeError ({proj_folder}/node_modules/multer/lib/make-error.js:12:13)
at wrappedFileFilter ({proj_folder}/node_modules/multer/index.js:39:19)
at Busboy.<anonymous> ({proj_folder}/node_modules/multer/lib/make-middleware.js:112:7)
at emitMany (events.js:127:13)
at Busboy.emit (events.js:201:7)
at Busboy.emit ({proj_folder}/node_modules/busboy/lib/main.js:31:35)
at PartStream.<anonymous> ({proj_folder}/node_modules/busboy/lib/types/multipart.js:213:13)
at emitOne (events.js:96:13)
at PartStream.emit (events.js:188:7)
at HeaderParser.<anonymous> ({proj_folder}/node_modules/dicer/lib/Dicer.js:51:16)
at emitOne (events.js:96:13)
at HeaderParser.emit (events.js:188:7)
at HeaderParser._finish ({proj_folder}/node_modules/dicer/lib/HeaderParser.js:68:8)
at SBMH.<anonymous> ({proj_folder}/node_modules/dicer/lib/HeaderParser.js:40:12)
at emitMany (events.js:127:13)
at SBMH.emit (events.js:201:7)
updated HTML
<form accept-charset="UTF-8" class="form" name="form" ng-submit="$ctrl.submitForm(form)"
enctype="multipart/form-data">
...
<input ngf-select ng-model="$ctrl.paperFile" ngf-model-options="{allowInvalid: true}" name="paper" ngf-accept="'application/pdf'" required="" type="file" >
...
</form>
submitForm method
...
this.Upload.upload({
url:'/paperUpload',
method: 'POST',
file: this.paperFile,
fields:{
_id:this.user._id
}
})
.then(function(resp){
console.log('Success upload');
console.log(resp.data);
}, function(error){
console.log('fail upload');
console.log(error);
}, function(evt){
console.log('upload on progress');
console.log(evt);
});
Server route:
var express = require('express');
var multer = require('multer');
var router = express.Router();
var upload = multer({dest:'uploads/',
rename: function(fieldname, filename){
return filename+"_"+Date.now();
}});
router.post('/paperUpload', upload.single('paper'), uploadPaper);
...
//method to upload
export function uploadPaper(req,res){
res.status(204).end();
}
The folder 'uploads' is created, but the file is not uploaded and always returned fail
Any help is appreciated,
Thank you
Do these steps
npm install ng-file-upload
include ng-file-upload.min.js in your angular index .html
Use this example to copy form to your angular page from where u want to upload file. -- http://jsfiddle.net/0m29o9k7/
Copy this code outside of any form which is already there:
Change this url from example code to where you want to upload files -- url: 'https://angular-file-upload-cors-srv.appspot.com/upload',
In your server.js or app.js which ever you are using as (node server.js) to start app add these lines
var crypto = require('crypto');
var mime = require('mime');
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'app/app-content/images/')
},
filename: function (req, file, cb) {
crypto.pseudoRandomBytes(16, function (err, raw) {
cb(null, raw.toString('hex') + Date.now() + '.' + mime.extension(file.mimetype));
});
}
});
var upload = multer({ storage: storage });
// make '/app' default route
app.post('/', upload.any(), function (req, res) {
res.send(req.files);
});
Change 'app/app-content/images/' this where you want your uploaded file will be
This code points your file upload url to index of your node server.
then you will be able to see the uploaded file.
Try this, I have never seen the 'file' or 'fields' options of Upload.upload. Do you know those are working? I just overcame a similar error by adding the arrayField to my POST call.
if you get the same error try removing 'fields' and adding a new key to the data object with the _id you wish to pass along.
this.Upload.upload({
url:'/paperUpload',
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data ; boundary = ----WebKitFormBoundaryvlb7BC9EAvfLB2q5'
},
arrayKey: '',
data: {
paper: this.paperFile,
},
fields:{
_id:this.user._id
}
})
.then(function(resp){
console.log('Success upload');
console.log(resp.data);
}, function(error){
console.log('fail upload');
console.log(error);
}, function(evt){
console.log('upload on progress');
console.log(evt);
});
Related
TASK OF CODE : The code telling multer to save file with pdf extension
PROBLEM : I am getting back error in response but the file getting saved inside the folder
const express = require("express");
const app = new express();
const multer = require("multer");
const upload = multer({
dest: "images", // destination to save the image
limits: 100000, // limiting the size of file to 1mb and 1 mb = 100000bytes
fileFilter(req, file, cb) {
if (!file.originalname.endsWith("pdf")) {
cb(new Error("please upload PDF file extension")); // sending error
}
cb(undefined, true);
// 3 type of call backs
// cb(new Error('please upload PDF file extension'));// sending error
// cb(undefined,true)// undefined means no error and true means accepting file
// cb(undefined,false)// undefined means no error and true means rejecting file
},
});
app.post("/upload", upload.single("upload"), (req, res) => {
res.send();
});
app.listen(8000, () => {
console.log("server fired off");
});
The error message is correct as per what I want
enter image description here
But the file is getting saved in images folder which should not be saved because I am sending jpg extension
Looks like the problem is that the code execution continues after cb(new Error(..), thus cb(undefined,true) gets called as well, telling multer that everything is ok. Change it to:
if (!file.originalname.toLowerCase().endsWith("pdf")) {
return cb(new Error("please upload PDF file extension")); // sending error
}
cb(undefined, true);
Note that I also used .toLowerCase() just to be sure that files with a.PDF extension get uploaded.
I'm making this new function called checkFileType()
function checkFileType(file, cb){
const filetypes = /pdf/;
const extname = filetypes.test(file.originalname.split('.')[file.originalname.split('.').length-1]);
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
}else{
cb(error = 'message : please upload PDF file extension');
}
}
And I implemented it like this
const upload = multer({
dest: "images", // destination to save the image
limits: 100000, // limiting the size of file to 1mb and 1 mb = 100000bytes
fileFilter(req, file, cb) {
checkFileType(file, cb);
},
});
I send a file from a client-side to the server-side:
const formData = new FormData();
formData.set("file", file, file.name);
let ip = location.host;
$.ajax({
processData: false,
contentType: false,
type: "POST",
url: http() + ip + "/fileUpload",
data: formData,
success: %callback%,
error: function (err) {
return false;
}
});
The server-side (Node.js) catches this request via Express.js:
app.post("/fileUpload", function (req, res) {…}
Now, I want to access the uploaded file on a server-side in debugger.
Since the file is wrapped with FormData, I try to use req.body.get("file") following the FormData.get() API, and expect to get the file in base64/blob, but instead I get:
Uncaught TypeError: req.body.get is not a function
How can I access an uploaded file, which is wrapped by FormData from a POST-request?
The file is 100% uploaded to the server-side, since multer is capable of serializing the request to the file.
P.S. I've checked already How to send FormData objects with Ajax-requests in jQuery?, but there the answer demonstrates just a way to send a request, not to proceed the request.
I can propose a sort of a workaround. You can access it with multer as described in the tutorial
https://www.npmjs.com/package/multer
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
if you prepare the formData object in the following manner:
let formData = new FormData();
formData.set("file",file, file.name);
you shall be able to fetch the file object on the server side with multer as follows:
const upload = multer({ storage: './../uploads/' }).single('file');
upload(req, res, function(err) {
console.log("The file: " + req.file);
console.log("The path: " + req.file.path);
});
I tried to receive the file and store it in the multer storage
Node js code
enter code here
app.post('/createLicence', upload.single('photo'),function(req, res ,next) {
// any logic goes here
console.log("filename" ,req.body.name)
if (!req.file) {
console.log("No file received");
return res.send({
success: false
});
} else {
console.log('file received');
var function_name = 'createLicence'
var arguments_array = [req.file.path,'Raghav','Mumbai','Approved']
invoke = require('/Users/sanjeev.natarajan/fabric-samples/fabcar/invoke.js');
invoke.invokechaincode(function_name,arguments_array)
return res.send({
success: true
})
}
});
but i am receiving no file is receivedi have send the request through postman
-
From : https://www.npmjs.com/package/multer
In order to use the multer package, you have first to define a few parameters so that it can work on your fileDirectory.
In your server.js :
let multer = require('multer');
let storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '/path/to/storage/')
},
filename: function(req, file, callback) {
callback(null, file.originalname + '-' + Date.now());
}
});
let upload = multer({
storage: storage
});
Now, configure your route
router.route('/your/payload')
.post(authController.isAuthenticated, upload.any(), albumController.postFile)
Note that upload.any() will allow you to upload multiple different formatted files at once. Feel free to use any other kind of upload.method() depending on your needs.
From this point, multer already is doing its job, however you might want to keep track of the files uploaded on your server.
So, in your own module, the logic is pretty much straight forward :
(I'm assuming that you're using mongoose models since you're not giving much information, but that's not the relevant part anyway)
exports.postFile = async (req, res) => {
if (!req || !req.files || !req.files[0]) return res.status(400).send("Bad request.");
for (let i = 0; req.files[i]; i++) {
await File.create({
path: req.files[i],
originalName: req.files[i].originalName,
mimetype: req.files[i].mimetype,
owner: req.user.userId
}, (err, file) => {
if (err) console.log("Something went wrong: " + err); else {
// Do something with file
}
});
}
return res.status(418).send("I'm a teapot.");
}
This configuration and middleware use is ONLY for testing purpose, never ever let anyone upload something to your server without carefully handle that uploading process (file integrity, resource management, ...). An open uploading system can become a very wide backdoor getting straight to your server.
Hope this helps,
regards.
I've been trying to upload an image for a while now, but req.file is still undefined. Can someone see why?
this is my page. I am able to pick an image when I click the '+' glyphicon, but on the server side req.file is still empty.
EJS file
input[type="file"] and input[type="submit"] have css styles display: none
<form action="/profile/addProfilepicture" method="post" id="form" enctype="multipart/form-data">
<span id="upload" class="glyphicon glyphicon-plus-sign"></span>
<label for="profilePic"></label>
<input id=profilePic type='file' />
<input type="submit">
</form>
<img class="profileImg"
src="<%="images/pexels-photo-370799.jpeg"%>"
alt="fail">
Client JS file
When I click the '+'glyphicon it lets me pick an image. When I do this, this will trigger the form to submit and send a post request.
$("#upload").on('click',function() {
$("input[type='file']").click();
});
$('input[type="file"]').change(function (e) {
$("input[type='submit']").click()
});
server side JS
On the server side it stops at:
TypeError: Cannot read property 'filename' of undefined
at C:\Users\Tijl Declerck\Desktop\projects\digitalNomadApp\routes\profile.js:27:38
at Immediate._onImmediate (C:\Users\Tijl Declerck\Desktop\projects\digitalNomadApp\node_modules\multer\lib\make-middleware.js:53:37)
at runCallback (timers.js:793:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)
The console.logs I tried gave me this: req.body returns an empty object and req.file returns undefined.
var express = require('express');
var router = express.Router();
var multer = require('multer');
var User = require('../models/Users');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/profilePics')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({ storage: storage }).single('myImage');
router.post('/addProfilePicture', function (req, res) {
var profilePicUrl = '';
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
} else {
console.log(req.file);
profilePicUrl = req.file.filename;
User.update({username: req.user.username}, {'profilePic.uploaded': true, 'profilePic.link': profilePicUrl}, function(err, doc){
console.log('THIS IS DONE')
});
}
});
});
You have to provide a name to your file input and it should match the single method's name, this is from multer doc:
.single(fieldname)
Accept a single file with the name fieldname. The single file will be
stored in req.file.
This is not well documented but fieldname refers to input name attribute
EJS file
<input id='profilePic' name='myImage' type='file' />
Express
...
var upload = multer({ storage: storage }).single('myImage');
...
I have big headache to combine angular file upload plugin with multer to make it fully SPA. I stucked on uploading multiple files through multer.
This is how my multer options looks like: (node route.js file)
var upload = multer({
storage: storage,
limits: {
//fileSize: 819200
}
}).array('myFile');
this is my POST: (node route.js file)
router.post('/add/file', function(req, res, next) {
upload(req,res,function(err) {
console.log(req.files);
if(err) {
console.log("Error uploading file.");
}
});
});
this is inside my angular controller:
var uploader = $scope.uploader = new FileUploader({
url: 'http://localhost:3000/add/file',
alias: 'myFile'
});
uploader.filters.push({
name: 'imageFilter',
fn: function(item /*{File|FileLikeObject}*/, options) {
var type = '|' + item.type.slice(item.type.lastIndexOf('/') + 1) + '|';
return '|jpg|png|jpeg|bmp|gif|'.indexOf(type) !== -1;
}
});
It adds only 1st file and stucks - I don't get any error it just stucks - whole page works and I can send files again, but again only 1st file will be uploaded. Console shows that req.files have only 1 file (that first one)
I couldn't find any tutorial or anything on the Internet with angular-file-upload plugin, that's why I ask you guys
Not sure if you figured this out yet or not, but with sending multiple files over, the 'uploadAll' function will not send the next file until it receives a response back from the server. So the route should look like this. I also saw somewhere in the documentation that the response needs to be json...haven't tested whether or not this is true though
router.post('/add/file', function(req, res, next) {
upload(req,res,function(err) {
console.log(req.files);
if(err) {
console.log("Error uploading file.");
} else {
res.status(200).json({response: 'some response...'})
}
});
});