NodeJS & Angular Image upload - angularjs

Tried lot of available Internet tutorials, but I still can't make it functional. I use multer module in NodeJS and ng-file-upload in AngularJS. I made two helpers with multer settings( because I have two scenarios and both uploads must go to different folders). Backend files are in APP/APP_BACK/ folder, so in destination path I go back one folder and enter APP_FRONT/images/header. Here is one helper snippet (/helpers/uploadHeader.js):
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, '../APP_FRONT/images/header/');
},
filename: function (req, file, callback) {
var ext = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
callback(null, file.fieldname + '-' + '.' + ext);
}
});
var upload = multer(
{storage: storage}
);
var helper = {
upload: upload
};
module.exports = helper;
Here is router file:
var headerHelper= require('../helpers/uploadHeader');
router.post('/header', headerHelper.upload.single('header'), function(req, res, next) {
headerHelper.upload(req, res, function(err){
if(err){
res.status(400).send(err);
console.log(err);
return;
}
res.status(200).send({ code:200, message:'Header upload successful!' });
});
});
"header" would be name of input form or key value in Postman.
In Angular, I injected 'ngFileUpload' module in app, and injected 'Upload' service into desired controller and used it inside uploadHeader() function which is bound on button inside form on clientside:
$scope.uploadHeader = function (file) {
Upload.upload({
url: 'http://localhost:3003/upload/header',
method: 'POST',
file: file
}).then(function (response) {
console.log('Success ' + response.config.data.file.name + 'uploaded. Response: ' + response.data);
}, function (error) {
console.log('Error status: ' + error.status);
}, function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
});
I tried with Postman:
Postman request SS
And get this error:
"Error: ENOENT: no such file or directory, open 'c:\Users\Username\Desktop\APP\APP_FRONT\images\header\header.jpg'"
When I try from clientside, I get these errors:
"Error: Unexpected field at makeError "
and
"TypeError: dbg is undefined"
I already consulted Google, tried some of tutorials but got stuck on this.

In your router, you are telling multer to look for an object called "header" in the mulitpart form data. Your angularJS code is adding it as an object called 'file'.
If you change the line in your router from:
router.post('/header', headerHelper.upload.single(**'header'**), function(req, res, next) {
//.....
}
to:
router.post('/header', headerHelper.upload.single(**'file'**), function(req, res, next) {
//....
}
It should do the trick.

EDIT: solution found
Actually, it was problem in filename function in multer's storate settings. In above post, I appended datetimestamp and file extension to fieldname:
filename: function (req, file, callback) {
var datetimestamp = moment().format('DD-MM-YY_HH:mm:ss');
var filename = file.originalname;
var ext = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
callback(null, file.fieldname + '-' + datetimestamp + '.' + ext);
}
For some reason, it couldn't finish upload and I got error.
I changed filename function to return only file's originalname :
filename: function (req, file, callback) {
callback(null, file.originalname);
}
and now everything is OK. But now, question is, why custom filename doesn't work?

Related

passing extra info to multer Upload API

I am using the multer way for upload in node.js, explained in detail here https://ciphertrick.com/2015/12/07/file-upload-with-angularjs-and-nodejs/
I am trying to pass another information with requested data which is called invoiceId:
Upload.upload({
url: 'http://localhost:4000/api/ubiq/listInvoiceAttachedFiles/attach', //webAPI exposed to upload the file
data: {file: file, invoiceId:invoiceId} //pass file as data, should be user ng-model
}).then(function (resp) { //upload function returns a promise
if (resp.data.error_code === 0) { //validate success
$window.alert('Success ' + resp.config.data.file.name + ' uploaded');
console.log(resp.config.data.file); ....etc
But I am getting req.body empty on the server side:
/** API path that will upload the files */
server.post('/api/ubiq/listInvoiceAttachedFiles/attach', function(req, res) {
console.log(req.body);
security.verifyPermission("/api/ubiq/listInvoiceAttachedFiles/attach", req.session.currentUser, true /*isInSession*/).then(function (successInfo) {
if (!successInfo.isAllowed) {
console.log('not allowed');
return res.json(apiHelp.notAllowed());
}
What am I doing wrong?
Multer re-attaches the body object within the callback of the upload function.
Your data should be available after this line.
https://github.com/rahil471/file-upload-with-angularjs-and-nodejs/blob/master/server/app.js#L35
The following snippet is my working code and I have tested and used multiple time in my application.
Multer have two storage engines:
DiskStorage
MemoryStorage
I have used DiskStorage, which gives more control over disk storage on file.
var express = require("express");
var app = express()
var router = express.Router();
var multer = require("multer");
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, dirPath);
},
filename: function (req, file, cb) {
var datetimestamp = Date.now() + Math.floor(Math.random() * (1 - 99999999999 + 1)) + 9999999999999;
cb(null, datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length - 1].toLowerCase());
}
});
var infoUpload = multer({storage: storage});
router
.route(API_PATH)
.post(infoUpload.array("file"), function (req, res) {
console.log(req.body);
});
Following are few links which give you a brief explanation.
http://derpturkey.com/node-multipart-form-data-explained/
http://alexkatz.me/posts/image-upload-with-node-and-multer/
Hope it may help to resolve an issue.

ng-file-upload send array into database

i want to upload array of data that i have by using ng-file-upload, but if i repeat it, my database only got last image, can anyone know what's wrong with my code?
Here is my code
$scope.send = function () {
console.log("aaa " + $scope.file);
console.log("files total: " + $scope.portfolio_images_array.length);
if ($scope.file && $scope.file1 && $scope.portfolio_images_array.length > 0) {
Upload.upload({
url: 'upload_url',
data: {
user_id: 7,
profile_photo: $scope.file,
id_card_photo: $scope.file1,
name: $scope.name,
introduction: $scope.introduction,
portfolio_image: $scope.portfolio_images_array
}
}).then(function (resp) {
console.log('Success ');
}, function (resp) {
console.log('Error status: ' + resp.status);
}, function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ');
});
}
};
portfolio_image should get all the images, but now it only get the last image from portfolio_images_array.
For adding the data to portolio_image_array, i use this code:
$scope.addFiles = function (file) {
console.log(file);
$scope.portfolio_images_array.push(file);
}
Please help me with this, Thanks
i found an answer, we just need to add arrayKey, here is the code
$scope.send = function () {
if ($scope.file && $scope.file1 && $scope.portfolio_images_array.length > 0) {
Upload.upload({
url: 'uploadURL',
arrayKey: '',
data: {
user_id: 7,
profile_photo: $scope.file,
id_card_photo: $scope.file1,
name: $scope.name,
introduction: $scope.introduction,
portfolio_image: $scope.portfolio_images_array
}
}).then(function (resp) {
console.log('Success ');
}, function (resp) {
console.log('Error status: ' + resp.status);
}, function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ');
});
}
};
Upload.upload is in fact a http request.
What you are doing is looping through all your array and make a http request for each file.
This is already really bad, and you should send the array directly with a syntax like this :
Upload.upload({
url: storageService.Address_of_your_server + '/your/route',
file: file,
arrayKey: '',
data: data,
}).success(function (resp) {
cbfun(resp);
});
}
Where file is your array of files and data your json of data you want to send in the same time.
The fact you are receiving only the last image is a bit weird.
Two ideas comes to my mind :
Upload service stop the request if he is called another time while the request is not finished.
As you are looping in a synchronous way while using asynchronous functions into your loop. Consequently, the javascript excecutes very quickly the loop and the i is incremented before even sending the requests.
But I don't really think this is the second possibility, as this case should only happen if you use the i variable in the callback of your promise (then).
EDIT :
Why you need to use ArrayKey :
This is to accommodate server implementations expecting array data object keys in '[i]' or '[]' or ''(multiple entries with same key) format.
So if you are not using it, the server part won't understand the format of the request.
According to the official doc.
Also, if you are using NodeJS, it is almost sure you are using the multer middleware in the back-end.
Consequently, you should have a route like this in the back :
router.post('/your/route', upload.array('file'), function(req, res){
//Your files are in this variable
var files = req.files
}

nodejs write simple image blob - Upload.dataUrltoBlob

Simple question. How do I save a image blob in Nodejs from angular.
AngularSide:
$scope.upload = function (dataUrl, picFile) {
Upload.upload({
url: 'http://test.dev:3000/register/user/uploads',
data: {
file: Upload.dataUrltoBlob(dataUrl, picFile.name)
},
}).then(function (response) {
$timeout(function () {
$scope.result = response.data;
});
}, function (response) {
if (response.status > 0) $scope.errorMsg = response.status
+ ': ' + response.data;
}, function (evt) {
$scope.progress = parseInt(100.0 * evt.loaded / evt.total);
});
}
nodejs side: Do I need middleware here? if so which one should I use?
router.post('/user/uploads', multipartMiddleware, function(req, resp) {
var newPath = "/Users/testUser/test_hold_files/" + req.files.file.originalFilename;
fs.writeFile(newPath, req.files.file, function(err) {
if (err) {
console.log("Data Error ");
return console.error(err);
}
});
res.status(200).jsonp({status: "status: success "});
});
right now this just writes out the file with correct name but its empty.
You used to be able to access the uploaded file through req.files.imageName and then you would fs.readFile from tmp and write it permanently, which is no longer the case in express 4.0
In Express 4, req.files is no longer available on the req object by default. To access uploaded files on the req.files object, use multipart-handling middleware like busboy, multer, formidable, multiparty, connect-multiparty, or pez.
Soooooooo, you can feel free to use which ever one of those middlewares names above and then follow their API for dealing with uploaded files like images. Hope this helps, enjoy.
Ok,
After a long time of messing with this stuff. I found an answer. It does load the file in my folder.
I feel this is only partial since it does not resize the actual file smaller. It is what is selected with https://github.com/danialfarid/ng-file-upload. I used the
Upload.upload({
url: 'http://test.dev:3000/register/user/uploads',
data: {
file: Upload.dataUrltoBlob(dataUrl, picFile.name)
},
This did zoom into the file on selected image. It did not make the actual file size smaller. I am still looking into this issue.
var formidable = require('formidable'),
util = require('util'),
fs_extra = require('fs-extra');
This is my post to accept images.
router.post('/user/uploads', function (req, res){
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
form.on('end', function(fields, files) {
/* Temporary location of our uploaded file */
var temp_path = this.openedFiles[0].path;
/* The file name of the uploaded file */
var file_name = this.openedFiles[0].name;
/* Location where we want to copy the uploaded file */
var new_location = "/Users/testUser/test_hold_files/";
fs_extra.copy(temp_path, new_location + file_name, function(err) {
if (err) {
console.error(err);
} else {
console.log("success!")
}
});
});
});
I have also noticed that I can view the file in chrome but not load it into gimp. Gimp gives me a file error.
Small steps I guess.
Maybe Datsik can give us some insight on what is going on here.
https://stackoverflow.com/users/2128168/datsik
Phil

Using Node Buffer or fileStream with formData file upload

Update: I have more or less solved the problem using multipart (app.use(multipart({uploadDir: __dirname + '/../uploads'}))from these instructions), but still don't know why my original code (below) fails.
There have been numerous variations on this question, and I have tried the ideas there without success. I'm using a file uploading directive (and have since tried another open source alternative) to send a binary file to a node server, that runs the following code (based on an SO answer I can't now refind):
exports.receive = function(req, res) {
var fitFileBuffer = new Buffer('');
// req.setEncoding("binary"); //doesn't help
req.on('data', function(chunk) {
fitFileBuffer = Buffer.concat([fitFileBuffer, chunk]);
});
req.on('end', function() {
fs.writeFileSync(
"today2.fit",
fitFileBuffer,
'binary');
res.send(200);
});
};
If I upload today.fit and compare to today2.fit, they have the same Kb of data, but are not identical, and subsequent code fails to process the file. Given that this happens with two pieces of third party code I suspect the problem lies with my code.
Here are the details from the client side of the POST being made
In the end, when I realised that I wanted to avoid saving to disk, I modified generalhenry's code with some stuff from busyboy's site and my own use of a Buffer:
exports.receive = function (req, res, next) {
var busboy = new Busboy({ headers: req.headers });
var fileBuffer = new Buffer('');
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
file.on('data', function(data) {
console.log('File [' + fieldname + '] got ' + data.length + ' bytes');
fileBuffer = Buffer.concat([fileBuffer, data]);
});
file.on('end', function() {
console.log('File [' + fieldname + '] Finished');
genXmlFromString(fileBuffer.toString(), function(data) {
res.json(data);
});
});
});
busboy.on('finish', function() {
console.log("'finish'");
});
req.pipe(busboy);
};
UPDATE: the client post details helped. You're not posting a file stream (which would have worked) you're posting a form stream. The good news is there are good modules for handling form streams.
You'll need to pipe the request stream into a form handling stream (such as busboy) which will handle the ------WebKitFormBoundary. . . part and them give you the file(s) as stream(s)
https://github.com/mscdex/busboy
var Busboy = require('busboy');
exports.receive = function(req, res, next) {
var busboy = new Busboy({ headers: req.headers });
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
var fileWriteStream = fs.createWriteStream('today2.fit');
file.pipe(fileWriteStream);
});
busbody.on('finish', function() {
res.send(201);
});
req.pipe(busboy);
};

Upload camera file using angular js to sails.js server

I'm trying to make a simple file upload using angular and sails.i am fairly new to all of these technolegies.could any of you please tell me what is wrong with my code?
I have created an api called file using the command: "sails generate api file" in my sails server and in the controller i pasted this code(https://github.com/sails101/file-uploads/blob/master/api/controllers/FileController.js#L15):
/**
* FileController
*
* #description :: Server-side logic for managing files
* #help :: See http://links.sailsjs.org/docs/controllers
*/
module.exports = {
/**
* `FileController.upload()`
*
* Upload file(s) to the server's disk.
*/
upload: function (req, res) {
// e.g.
// 0 => infinite
// 240000 => 4 minutes (240,000 miliseconds)
// etc.
//
// Node defaults to 2 minutes.
res.setTimeout(0);
req.file('avatar')
.upload({
// You can apply a file upload limit (in bytes)
maxBytes: 1000000
}, function whenDone(err, uploadedFiles) {
if (err) return res.serverError(err);
else return res.json({
files: uploadedFiles,
textParams: req.params.all()
});
});
},
/**
* `FileController.s3upload()`
*
* Upload file(s) to an S3 bucket.
*
* NOTE:
* If this is a really big file, you'll want to change
* the TCP connection timeout. This is demonstrated as the
* first line of the action below.
*/
s3upload: function (req, res) {
// e.g.
// 0 => infinite
// 240000 => 4 minutes (240,000 miliseconds)
// etc.
//
// Node defaults to 2 minutes.
res.setTimeout(0);
req.file('avatar').upload({
adapter: require('skipper-s3'),
bucket: process.env.BUCKET,
key: process.env.KEY,
secret: process.env.SECRET
}, function whenDone(err, uploadedFiles) {
if (err) return res.serverError(err);
else return res.json({
files: uploadedFiles,
textParams: req.params.all()
});
});
},
/**
* FileController.download()
*
* Download a file from the server's disk.
*/
download: function (req, res) {
require('fs').createReadStream(req.param('path'))
.on('error', function (err) {
return res.serverError(err);
})
.pipe(res);
}
};
next, i have made a function that takes a photo and displays it.now what i want to do is to send that file into my server and store it. here is my code:
$scope.sendPost = function() {
getPosts.getFoo('http://10.0.0.3:1337/user/create?name=temporaryUser&last_name=temporaryLastName4&title=' +
shareData.returnData()[0].title + '&content=' + shareData.returnData()[0].content +
'&image=' + /*shareData.returnData()[0].image*/ + '&category1=' + category1 + '&category2=' +
category2 + '&category3=' + category3 + '&category4=' + category4 + '&category5=' + category5
).then(function(data) {
$scope.items = data;
console.log(shareData.returnData()[0]);
});
$scope.upload = function() {
var url = 'http://localhost:1337/file/upload';
var fd = new FormData();
//previously I had this
//angular.forEach($scope.files, function(file){
//fd.append('image',file)
//});
fd.append('image', shareData.returnData()[0].image);
$http.post(url, fd, {
transformRequest:angular.identity,
headers:{'Content-Type':undefined
}
})
.success(function(data, status, headers){
$scope.imageURL = data.resource_uri; //set it to the response we get
})
.error(function(data, status, headers){
})
}
}
Most of your codes are correct I think, but one thing which is very important that you should give the same "Name" of the data both on your front-end and the back-end.
In your case, the upload data name inside your back-end codes is "avatar":
req.file('avatar')
.upload({
// You can apply a file upload limit (in bytes)
maxBytes: 1000000
}, function whenDone(err, uploadedFiles) {
if (err) return res.serverError(err);
else return res.json({
files: uploadedFiles,
textParams: req.params.all()
});
});
However, inside the front-end codes the post data name is "image".
fd.append('image', shareData.returnData()[0].image);
I think this is should be the major issue which stopped your application.
Here's a working version of how to use angular to upload file remotely than API http://jsfiddle.net/JeJenny/ZG9re/ which is working for me.
Below is my backend codes for you reference:
// myApp/api/controllers/FileController.js
module.exports = {
upload: function(req, res) {
var uploadFile = req.file('file')
//console.log(uploadFile);
uploadFile.upload({
dirname: sails.config.zdbconf.attachmentPath,
maxBytes: 100000000
}, function(err, files) {
if (err)
return res.serverError(err);
return res.json({
message: files.length + ' file(s) uploaded successfully!',
files: files
});
});
}
};

Resources