I am posting to a formidable form from AngularJS, but the form does not parse. I do not use the express.bodyParser(), which I understand often is a problem.
serverside:
...
var form = new formidable.IncomingForm();
console.log(form); //this prints
//Get access to the other fields of the request.
form.parse(req, function (err, fields, files) {
console.log("parsing.."); //this does not print
console.log(fields);
console.log(files);`
...
client-side:
...
$http({
method: 'POST',
url: '/api/ad',
data: message, // your original form data,
transformRequest: formDataObject, // this sends your data to the formDataObject provider that we are defining below. `[see this](https://stackoverflow.com/questions/17629126/how-to-upload-a-file-using-angularjs-like-the-traditional-way)`
headers: {'Content-Type': undefined}
})
.success(function(data, status, headers, config){
deferred.resolve(data); `
...
When posting form Angular with the formData object, I have to set the content/type to undefined as mentioned in the comment here. Might that be a problem for formidable?
I have been using hours to try to figure this out with no luck. I would be very grateful for any answers!
I figured out this. I had to bind the files to the controller, as well as using the angular.identity function. Based on this great explanation I added the following to my angular code in order to bind files to the controller: `
myApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;`
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
and the following to post my form to the url I wanted:
myApp.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(data, uploadUrl){
var fd = new FormData();
angular.forEach(data, function(value, key) {
fd.append(key, value);
});
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(retObj){
console.log(retObj);
})
.error(function(retObj){
console.log(retObj);
});
}
}]);
In the submit-function in the controller I now add:
var formData = $scope.newForm;
var uploadUrl = '/api/add';
fileUpload.uploadFileToUrl(formData, uploadUrl);
and in my html, files-inputs gets the following tag in order to use the created file-model:
<input type="file" file-model="newForm.image1">
and my text inputs are just binded as before:
<input type="text" ng-model="newForm.title">
Hope this helps someone else :)
Related
I'm just trying to send HTML file upon POST request. I'm 100% sure it was working an hour ago. Since then, I cannot figure out why it's not working all of a sudden!
Server Router:
const express = require('express');
const router = express.Router();
const cors = require('cors');
const path = require('path');
const auth = require('../middleware/auth.js');
// HOME ROUTE
router.options('/', cors());
router.get('/', cors(), (req, res) => {
res.status(201).sendFile(path.resolve(__dirname, '../', '../', 'public', 'index.html'));
});
router.post('/', cors(), (req, res) => {
res.status(201).sendFile(path.resolve(__dirname, '../', '../', 'view', 'manager.html'));
});
There's no error from server.
index.html
<form method="POST" autocomplete="off">
<input id="username" type="text" name="username" placeholder="Username" onchange="updateUsername(event)"><br>
<input id="password" type="password" name="password" placeholder="Password" onchange="updatePassword(event)"><br>
<button onclick="submitFunc(event)">LOGIN</button>
</form>
<script>
let username_value = document.querySelector('#username').value;
let password_value = document.querySelector('#password').value;
function updateUsername(e) {
username_value = e.target.value;
}
function updatePassword(e) {
password_value = e.target.value;
}
async function submitFunc(e) {
e.preventDefault();
let response = await fetch('/', {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({
username: username_value,
password: password_value
})
});
console.log(response);
}
Please note that the login logic itself is not an issue. I altered my code a lot due to this issue I have.
Upon sending POST request to '/', This is the response that logs in client console:
So the fetching itself seems to work just fine. It's just that new HTML file is not replacing the current HTML file. How would I go about fixing this?
You need to actually read the response. await fetch(...) just gets the headers and leaves a readableStream sitting there with the content waiting for you to read the actual content with response.json() or response.text() depending upon the data type you're expecting.
Change to this:
async function submitFunc(e) {
e.preventDefault();
try {
let response = await fetch('/', {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
body: JSON.stringify({
username: username_value,
password: password_value
})
});
// this assumes the response is text or html,
// use response.json() if the response is json
let data = await response.text()
console.log(data);
} catch(e) {
console.log(e);
// decide what to do here if there was an error with the fetch() call
}
}
You can see the various different methods available for reading the body contents here on MDN.
Also, if you're making a request with fetch(), the response from your server will just come back to your Javascript in your web page. It will NOT automatically display in the browser. If you want it to display in your browser, then either let the form post natively (without Javascript) or you will have to manually code your Javascript to receive the response and then insert it into the page content yourself.
We want to implement a functionality to upload multiple files into ExpressJS server and return all its unique urls, by an ajax call.
Following is the sample code in my front end:
var formData = new FormData();
for (var i = 0; i < nameId.length; i++) {
if($(nameId[i])[0].files[0]){
formData.append(nameId[i], $(nameId[i])[0].files[0], $(nameId[i])[0].files[0].name);
}
}
$.ajax({
url: '/upload-files',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(data){
console.log('upload successful!');
console.log(data);
}
});
And in our router we have following code snippet to accept the request and store the file:
router.post('/upload-files',function(req, res, next){
var form = new formidable.IncomingForm();
form.multiples = true;
form.uploadDir = path.join(__dirname, '/uploads');
form.on('file', function(field, file) {
console.log("File incoming");
fs.rename(file.path, path.join(form.uploadDir, file.name));
});
form.on('error', function(err) {
console.log('An error has occured: \n' + err);
});
form.on('end', function() {
res.end('success');
});
});
But, nothing ever happened in the router. Request is coming inside the router but after that nothing.
Is there anything wrong here? We are not getting any error in server side and in client side after long wait request is failing.
Please suggest.
Thanks
I was able to resolve it by adding & updating following code snippets:
In the upload request processing function added code :
form.parse(req);
And in the app.js updated following code snippet to:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}));
to (To process multipart data seperatly)
app.use(bodyParser.json())
.use(bodyParser.urlencoded());
NOTE Due to the change in parser following warning message is coming while starting the server:
body-parser deprecated undefined extended: provide extended option
My problem was solved by changing my ajax sending code as follow:-
$.ajax({
url: '/postroute',
type: 'POST',
processData: false,
contentType: false,
cache: false,
data: formData,
enctype: 'multipart/form-data',
success: function(){
console.log('Uploaded sucessfully');
}
});
I am trying to replicate a login form's behaviour through koa.
The login form does:
<form id="loginForm" method="post" action="http://myaddress:3000/auth" enctype="multipart/form-data">
I'm using the koa request and form-data modules:
var form = new FormData();
form.append('identification', 'userId');
form.append('password', 'userPassword');
var options = {
url: DB_SERVER_URL + 'auth',
method: 'POST',
formData: form
};
var response = yield request(options);
console.log('response.statusCode: ' + response.statusCode);
But I always get a 400 response.
I've tried just using form.submit(DB_SERVER_URL + 'auth', function(err, res) { ... } which works, but I like koa's yield functionality and ideally I want to avoid having to deal with callbacks.
Any ideas?
Koa accepts multiple yield inputs that can be obtained from your current code more or less easily depending on your current setup:
a promise. As form-data doesn't seem to use them, we'll create one with Q
var Q = require('q');
var promise = Q.ninvoke(form, "submit", DB_SERVER_URL + 'auth');
var response = yield promise;
console.log('response.statusCode: ' + response.statusCode);
or a thunk, a wrapper function as you used in your answer, but there are libraries that can handle the wrapping for you (here, thunkify-wrap):
var thunkify = require('thunkify-wrap');
var submit = thunkify(form.submit, form); // the context is needed in this case
var response = yield submit(DB_SERVER_URL + 'auth');
console.log('response.statusCode: ' + response.statusCode);
I ended up using form.submit(DB_SERVER_URL + 'auth', function(err, res) { ... }, but wrapped the callbacks so I could use yield to maintain a synchronous control flow.
Here's my wrapper for the callback to the form.submit to receive the response:
function makeLoginRequest(formData) {
var form = new FormData();
form.append('identification', formData.identification);
form.append('password', formData.password);
var DB_SERVER_URL = 'http://myurl:3000/';
return function(callback) {
form.submit(DB_SERVER_URL + 'auth', function(error, response) {
callback(error, response);
});
}
}
And here's my wrapper for the callback for receiving the response body:
function getLoginResponseData(response) {
return function(callback) {
response.on("data", function(chunk) {
callback(null, chunk);
});
}
}
This lets me use yield to maintain a synchronous control flow:
var response = yield makeLoginRequest(this.request.body);
console.log('response.statusCode: ' + response.statusCode);
var chunk = yield getLoginResponseData(response);
console.log("BODY: " + chunk);
I'm a node and koa beginner, so if you have a better way please let me know!
If you are using koa-request I was able to do this.
const request = require('koa-request');
const response = yield request({
method: 'POST',
url: 'https://whatsever.com',
form: {
itema: 'vala',
itemb: 'valb',
},
headers: {
'Content-type': 'application/x-www-form-urlencoded'
}
});
this.body = response.body;
If you need multipart look here: https://www.npmjs.com/package/request#multipartform-data-multipart-form-uploads.
Remember that koa-request wraps therequest module
I have problem uploading file using POST request in Node.js. I have to use request module to accomplish that (no external npms). Server needs it to be multipart request with the file field containing file's data. What seems to be easy it's pretty hard to do in Node.js without using any external module.
I've tried using this example but without success:
request.post({
uri: url,
method: 'POST',
multipart: [{
body: '<FILE_DATA>'
}]
}, function (err, resp, body) {
if (err) {
console.log('Error!');
} else {
console.log('URL: ' + body);
}
});
Looks like you're already using request module.
in this case all you need to post multipart/form-data is to use its form feature:
var req = request.post(url, function (err, resp, body) {
if (err) {
console.log('Error!');
} else {
console.log('URL: ' + body);
}
});
var form = req.form();
form.append('file', '<FILE_DATA>', {
filename: 'myfile.txt',
contentType: 'text/plain'
});
but if you want to post some existing file from your file system, then you may simply pass it as a readable stream:
form.append('file', fs.createReadStream(filepath));
request will extract all related metadata by itself.
For more information on posting multipart/form-data see node-form-data module, which is internally used by request.
An undocumented feature of the formData field that request implements is the ability to pass options to the form-data module it uses:
request({
url: 'http://example.com',
method: 'POST',
formData: {
'regularField': 'someValue',
'regularFile': someFileStream,
'customBufferFile': {
value: fileBufferData,
options: {
filename: 'myfile.bin'
}
}
}
}, handleResponse);
This is useful if you need to avoid calling requestObj.form() but need to upload a buffer as a file. The form-data module also accepts contentType (the MIME type) and knownLength options.
This change was added in October 2014 (so 2 months after this question was asked), so it should be safe to use now (in 2017+). This equates to version v2.46.0 or above of request.
Leonid Beschastny's answer works but I also had to convert ArrayBuffer to Buffer that is used in the Node's request module. After uploading file to the server I had it in the same format that comes from the HTML5 FileAPI (I'm using Meteor). Full code below - maybe it will be helpful for others.
function toBuffer(ab) {
var buffer = new Buffer(ab.byteLength);
var view = new Uint8Array(ab);
for (var i = 0; i < buffer.length; ++i) {
buffer[i] = view[i];
}
return buffer;
}
var req = request.post(url, function (err, resp, body) {
if (err) {
console.log('Error!');
} else {
console.log('URL: ' + body);
}
});
var form = req.form();
form.append('file', toBuffer(file.data), {
filename: file.name,
contentType: file.type
});
You can also use the "custom options" support from the request library. This format allows you to create a multi-part form upload, but with a combined entry for both the file and extra form information, like filename or content-type. I have found that some libraries expect to receive file uploads using this format, specifically libraries like multer.
This approach is officially documented in the forms section of the request docs - https://github.com/request/request#forms
//toUpload is the name of the input file: <input type="file" name="toUpload">
let fileToUpload = req.file;
let formData = {
toUpload: {
value: fs.createReadStream(path.join(__dirname, '..', '..','upload', fileToUpload.filename)),
options: {
filename: fileToUpload.originalname,
contentType: fileToUpload.mimeType
}
}
};
let options = {
url: url,
method: 'POST',
formData: formData
}
request(options, function (err, resp, body) {
if (err)
cb(err);
if (!err && resp.statusCode == 200) {
cb(null, body);
}
});
I did it like this:
// Open file as a readable stream
const fileStream = fs.createReadStream('./my-file.ext');
const form = new FormData();
// Pass file stream directly to form
form.append('my file', fileStream, 'my-file.ext');
const remoteReq = request({
method: 'POST',
uri: 'http://host.com/api/upload',
headers: {
'Authorization': 'Bearer ' + req.query.token,
'Content-Type': req.headers['content-type'] || 'multipart/form-data;'
}
})
req.pipe(remoteReq);
remoteReq.pipe(res);
I am trying to upload a photo via a POST request with the request module
According to the readme I should just be able to do this
var r = request.post("http://posttestserver.com/post.php", requestCallback)
var form = r.form()
form.append("folder_id", "0");
form.append("filename", fs.createReadStream(path.join(__dirname, "image.png")));
function requestCallback(err, res, body) {
console.log(body);
}
The problem is, this doesn't work. I get a reply from the test server saying it dumped 0 post variables.
I have confirmed that the server is in working condition with this little html page
<html>
<body>
<form action="http://posttestserver.com/post.php?dir=example" method="post" enctype="multipart/form-data">
File: <input type="file" name="submitted">
<input type="hidden" name="someParam" value="someValue"/>
<input type="submit" value="send">
</form>
</body>
</html>
So the question is, what am I doing wrong with the request module? Is there a better way to send multipart/form-data with node?
After some more research, I decided to use the restler module. It makes the multipart upload really easy.
fs.stat("image.jpg", function(err, stats) {
restler.post("http://posttestserver.com/post.php", {
multipart: true,
data: {
"folder_id": "0",
"filename": restler.file("image.jpg", null, stats.size, null, "image/jpg")
}
}).on("complete", function(data) {
console.log(data);
});
});
So I just got done wrestling with this myself and here is what I learned:
It turns out that neither request or form-data are setting the content-length header for the generated body stream.
Here is the reported issue: https://github.com/mikeal/request/issues/316
The solution posted by #lildemon gets around this by:
Generating the FormData object
Getting it's length
Making the request and setting the form object and content-length header explicitly
Here is a modified version of your example:
var request = require('request');
var FormData = require('form-data');
var form = new FormData();
form.append("folder_id", "0");
form.append("filename", fs.createReadStream(path.join(__dirname, "image.png")));
form.getLength(function(err, length){
if (err) {
return requestCallback(err);
}
var r = request.post("http://posttestserver.com/post.php", requestCallback);
r._form = form;
r.setHeader('content-length', length);
});
function requestCallback(err, res, body) {
console.log(body);
}
I have working code that does exactly what your question states, with one exception. My file content is appended this way:
form.append('file', new Buffer(...),
{contentType: 'image/jpeg', filename: 'x.jpg'});
To discover the final options argument I had to drill down into the source of form-data. But this gives me a working configuration. (Maybe it was what you were missing, but of course that will depend on the server.)
I tried also request and form-data modules and was unable to upload a file.
You can use superagent which works:
http://visionmedia.github.io/superagent/#multipart-requests.
var request = require('superagent');
var agent1 = request.agent();
agent1.post('url/fileUpload')
.attach('file',__dirname + "/test.png")
.end(function(err, res) {
if (err) {
console.log(err)
}
});
Try request module. It works like any other normal post request
var jsonUpload = { };
var formData = {
'file': fs.createReadStream(fileName),
'jsonUpload': JSON.stringify(jsonUpload)
};
var uploadOptions = {
"url": "https://upload/url",
"method": "POST",
"headers": {
"Authorization": "Bearer " + accessToken
},
"formData": formData
}
var req = request(uploadOptions, function(err, resp, body) {
if (err) {
console.log('Error ', err);
} else {
console.log('upload successful', body)
}
});