I can't figure out how to post through angular $http. Why? I'd like multer to parse and store my file and angular to get a copy when it is done. Better yet, I'd love to just let angular get a copy, then pass it down to the server.
I am able to upload a file using the snippets below:
// view (jade)
.container
form(action="/upload", method="POST", enctype="multipart/form-data")
input(type="file", name="f")
input(type="submit", value="Upload")
// post route (express)
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('f'), function(req, res) {
console.log(req.file);
});
Running this with any sample file, returns the json I want to the console and the image is stored successfully in the proper directory. However:
// view (jade)
form(enctype="multipart/form-data" ng-submit="uploadFile(file)")
input(type="file", name="f", ng-model="file")
input(type="submit", value="Upload")
// ctrl (angular)
$scope.uploadFile = function(file) {
console.log(file);
$http.post('/upload', file);
};
// post route (express)
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('f'), function(req, res) {
console.log(req.file);
});
This returns undefined on both console inputs, even though I didn't change the file, I only changed how it was sent.
This leads me two believe one of two things:
The data submitted is not what I think it is. If this is the case, what is it? Nothing prints on the log.
OR
The data submitted is altered in some way by angular. If this is the case, how? Again nothing prints on the log.
In the first case, by sending the data directly through the form to the server, you let html do the transformation magic under the hood.
In the second case, by posting through AngularJs via $http, you need to tell the middleware that is $http, the required transformation it needs to do to the request to match the attributes you passed to the form.
For having struggled with this myself for a while, here is an adaptation of the code I used (for $resource). But I think it should work for its underlying $http.
So in your controller:
$scope.uploadFile = function(file) {
console.log(file);
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {
'Content-Type': undefined},
enctype: 'multipart/form-data'
}
})
.success(function(){
})
.error(function(){
});
};
For that to work, you need to bind the file input field to the model. Of course, Angularjs chooses not to do that natively for some reasons.
In the jade view:
form(enctype="multipart/form-data" ng-submit....)
input(type="file", name="f", ng-model="file" on change='angular.element(this).scope().readFile(this)')
input(type="submit", value="Upload")
onchange is JavaScript and not AngularJs and it triggers the scope.readfile() function that we need to define:
In the controller:
$scope.readfile = function(elem) {
var file= elem.files[0];
var reader = new FileReader();
reader.onload = function() {
$scope.$apply(function(){
$scope.file = file;
$scope.imageUrl = reader.result // to display image via ng-Src
})
}
reader.readAsDataURL(file);
}
I think that should bring back some html magic back into angular when working with forms.
I suggest you look into JavaScript's onchange, FormData, and FileReader for code snippets like these and better documentation.
Related
Here are the steps my application is doing:
User uploads an image (.PNG) file in the browser.
Browser sends the raw image data to my server.
The server then saves the file in my file system.
The problem is that my file system complains that the file is not a PNG file when I try to open it.
I'm trying to localize where the problem occurs without success. When I look at the file data using VIM it looks the same to me, with the same number of lines and the file contents both start with:
<89>PNG^M
^Z
^#^#^#^MIHDR^#^#^BD^#^#^#Î^H^B^#^#^#P6<8a><9a>^#^#^#^CsBIT^H^H^HÛáOà^ ^#^#_zTXtRaw profile type APP1^#^#^H<99>ãJOÍK-ÊLV((ÊOËÌIåR^#^Cc^S. ^SK^SK£D^C^C^C^K^C^H04006^D<92>F#
...
However, the file sizes are different, with the file I'm writing from my server being larger. So obviously they are different in some way.
I tried doing diff file1 file2 in the command line and it just gives me binary files differ without showing me the difference... ?? Strange.
So I'm currently confused about how the files differ, and where in my code this difference gets introduced...
I feel I'm missing some crucial knowledge about how some things work here under the hood, and I would very much appreciate if someone smarter than me could help me out here.
Code
Client:
<input id="icon-button-file" type="file" onChange={handleChange} />
<label htmlFor="icon-button-file">
<Button/>
</label>
function handleChange(event: any) {
if (!event.target.files[0]) return
const file = event.target.files[0]
const reader = new FileReader()
reader.onload = function (e) {
const instance = axios.create({
baseURL: 'http://localhost:3000/',
timeout: 5000,
headers: { 'Content-Type': 'application/json' }
})
instance.post(
'image',
JSON.stringify({
imageData: e.target.result.toString()
})
).then(result => {
console.log(result)
})
}
reader.readAsBinaryString(file)
}
Server:
app.post(`http://localhost:3000/image`, (req, res) => {
fs.writeFile('img.png', req.body.imageData, (err) => {
console.log(err)
})
})
EDIT:
I made it work by sending the image content to the server as a dataUrl instead, using reader.readAsDataUrl(). This encodes the image data as a Base64 string, which seems like a common practice. However, I'm still curious why sending the raw data doesn't work.
You can use Formidable to handle files in nodejs easily. Good to hear that you got it running already. I hope this helps to someone else. this covers some basics
and yes another approach is to encode to Base64 and decode back to a file from the server side. cheers
var express = require('express');
var router = express.Router();
var fs = require('fs');
var formidable = require('formidable');
/* GET home page. */
router.get('/', function(req, res, next) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<form action="fileupload" method="post" enctype="multipart/form-data">');
res.write('<input type="file" name="filetoupload"><br>');
res.write('<input type="submit">');
res.write('</form>');
res.end()
});
router.post(`/fileupload`, (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var oldpath = files.filetoupload.path;
var newpath = './public/' + files.filetoupload.name;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
res.end();
});
});
})
module.exports = router;
Answering my own question.
The reason for the problem was that I was sending the image binary data over http which can corrupt the data.
See: https://stackoverflow.com/a/201510/6017605
Since base64 encodes it as text, it can safely be transmitted.
This also helped me understand the problem: https://www.jscape.com/blog/ftp-binary-and-ascii-transfer-types-and-the-case-of-corrupt-files
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
I am new to Node and I am trying to use app.post, everything works fine including the console log whenever the action is executed but it does not receive the data sent by the AJAX from main.js.
Here is a snipper of main.js which sends it:
$.ajax({
type: 'POST',
data: '{"title":'+ this.messageInput.value +'}',
url: 'http://localhost:3000/send'
});
Here is my config.js which should be receiving the data:
app.post('/send', function(req, res) {
console.log(req.body);
console.log('Send button clicked');
});
I am using bodyParser with express. So that is why I am using req.body. When I console log req.body I get undefined. When I console log req, I get a list of data but not what I sent.
I hope someone can help!
It's good that you are using body-parser, but have you defined a JSON parser in your app?
You didn't paste all of your code so add this if you don't already have it:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// create application/json parser --> This is probably what you are missing
var jsonParser = bodyParser.json()
// POST /api/users gets JSON bodies --> Note how we use a jsonParser in our app.post call
app.post('/send', jsonParser, function (req, res) {
console.log(req.body);
console.log('Send button clicked');
})
Also, instead of constructing your data manually, just create an object and send it (remember JSON is short for - Java Script Object Notation)
var myData = {};
myData.title = this.MessageInput.value;
Then in your Ajax code, just use myData:
$.ajax({
type: 'POST',
data: myData,
url: 'http://localhost:3000/send'
});
Note: most of this example is taken straight from the body-parser Github Page, it has plenty of well documented examples so if you're not sure how to use the module, check it out
I'm working on an application using Node JS, Formidable, and Cloudinary dependencies that lets users upload images to a Cloudinary database.
Users will upload images from a multipart form, and I want the fields to become variables to be passed to the Cloudinary upload function.
My code works but I'm having trouble passing form information as Cloudinary parameters.
Here is my current code, which passes the original file name:
router.post('/upload', 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) {
var temp_path = this.openedFiles[0].path;
var file_name = this.openedFiles[0].name;
cloudinary.uploader.upload(temp_path, function(result) { console.log(result) },
{ public_id: file_name });
})
But I don't want the original file name. Instead of var file_name I want var image_name which is provided by the user from the form
BTW, there's no need to handle end, since you're providing a callback to parse(). The callback is called when end is fired.
The text the user enters will be in fields.
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?