Put data and attachment to Cloudant DB IBM bluemix - node.js

I am trying to PUT my data within its attachment. I am doing it with NodeJS
Here is my code:
var date = new Date();
var data = {
name : obj.name,
serving : obj.serving,
cuisine : obj.cuisine,
uploadDate : date,
ingredients : obj.ing,
directions: obj.direction
} //Assume that I read this from html form and it is OK
db.insert(data, function(err, body){
if(!err){
console.log(body);
var id = body.id
var rev = body.rev
var headers = {
'Content-Type': req.files.image.type
};
var dataString = '#' + req.files.image.path;
var options = {
url: 'https://username:password#username.bluemix.cloudant.com/db_name/' + id + '/' + req.files.image.name +'?' + 'rev=' + rev,
method: 'PUT',
headers : headers,
body : dataString
}
console.log(options)
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
}
request(options, callback);
}
});
I am getting a 201 response after an image attachment has been sent. But in the cloudant dashboard I see "length": 38 of uploaded image which is impossible.
If I try to access uploaded image it gives:
{"error":"not_found","reason":"Document is missing attachment"}.
How I can fix this problem?

It looks like you are uploading the path to the image and not the contents of the image itself:
var dataString = '#' + req.files.image.path;
This will just upload the string '#xyz' where xyz is the path of the file. You need to upload the contents of the image. See the following for more information:
https://wiki.apache.org/couchdb/HTTP_Document_API#Attachments
I am not sure how to get the contents of the uploaded file from req.files. I believe req.files no longer works in Express 4, so I use multer:
https://github.com/expressjs/multer
This is how I upload a file to Cloudant that was uploaded to my app:
Client
<form action="/processform" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit" value="Submit">
</form>
Node.js Routing
var multer = require('multer');
...
var app = express();
...
var uploadStorage = multer.memoryStorage();
var upload = multer({storage: uploadStorage})
app.post('/processform', upload.single('myfile'), processForm);
Note: 'myfile' is the name of the file input type in the form.
Node.js Upload to Cloudant
function processForm() {
// insert the document first...
var url = cloudantService.config.url;
url += "/mydatabase/" + doc.id;
url += "/" + encodeURIComponent(req.file.originalname);
url += "?rev=" + doc.rev;
var headers = {
'Content-Type': req.file.mimetype,
'Content-Length': req.file.size
};
var requestOptions = {
url: url,
headers: headers,
body: req.file.buffer
};
request.put(requestOptions, function(err, response, body) {
if (err) {
// handle error
}
else {
// success
}
});
...
}

Related

Upload a file from dart client to Node server

I'm stuck with this issue : I can't get my upload to work:
This is a node.js code taht works with a standard <form><input type="file" name="toUpload/>
router.post('/sp/file', function (req, res) {
// File to be uploaded
console.log("###" + req.files);
var fileToUpload = req.files.toUpload;
//console.log(fileToUpload);
var dir = __dirname + "/files";
/* var dir = __dirname + "/files/" + Date.now();
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}*/
fileToUpload.mv( __dirname + "/files/" + fileToUpload.name, function (err) {
if (err) {
console.log("error: " + err);
} else
console.log("upload succeeded");
console.log(fileToUpload);
console.log(__dirname + "/files/" + fileToUpload.name);
uploadFilesStorj.uploadFile(__dirname + "/files/" + fileToUpload.name);
});
});
Now, when I try to upload a file through dart, I get stuck since the sent data is not in the same format:
class AppComponent {
void uploadFiles(dynamic files) {
if (files.length == 1) {
final file = files[0];
final reader = new FileReader();
//reader.onProgress.listen()
reader.onLoad.listen((e) {
sendData(reader.result);
});
reader.readAsDataUrl(file);
}
}
sendData(dynamic data) async {
final req = new HttpRequest();
req.onReadyStateChange.listen((Event e) {
if (req.readyState == HttpRequest.DONE &&
(req.status == 200 || req.status == 0)) {}
});
req.onProgress.listen((ProgressEvent prog) {
if (prog.lengthComputable)
print("advancement : " + (prog.total / prog.loaded).toString());
else
print("unable to compute advancement");
});
req.open("POST", "/sp/file");
req.send(data);
}
}
here's my dart angular front code
<input type="file" #upload (change)="uploadFiles(upload.files)"
(dragenter)="upload.style.setProperty('border', '3px solid green')"
(drop)="upload.style.setProperty('border', '2px dotted gray')" class="uploadDropZone" name="toUpload"/>
The data sent by this method is in the form :
Request payload:
data:text/html;base64,PGh0bWw+DQogICA8aGVhZD4NCiAgICAgIDx0aXRsZT5GaWxlIFVwbG9hZGluZyBGb3JtPC9
I passed a lot of time on it without success, can anybody help please
I finally found a way to post it as a multi-part form:
void uploadFiles() {
var formData = new FormData(querySelector("#fileForm"));
HttpRequest.request("/sp/file", method: "POST", sendData: formData).then((req) {
print("OK");
});
}
is used in conjunction with
<form id="fileForm">
<input type="file" #upload (change)="uploadFiles(upload.files)"
(dragenter)="upload.style.setProperty('border', '3px solid green')"
(drop)="upload.style.setProperty('border', '2px dotted gray')" class="uploadDropZone" name="toUpload"/>
</form>
You are writing the file content directly in the request body in the Dart variant. However the HTML form sends a request with multipart form encoding and embeds the file in there. That's also what your server expects.

Invoke Bluemix Workflow with Node.js app

I am able to invoke my workflow with the DevOps IDE, but not with Node.js app. My Node.js app code is:
var buffer = new Buffer(workflowConfig.credentials.user + ":" + workflowConfig.credentials.password,"ascii");
var base64Auth = buffer.toString('base64');
var baseURL = workflowConfig.credentials.url.substr(0, workflowConfig.credentials.url.lastIndexOf('/'));
var scriptFilepath = 'helloworld';
var wfTarget = 'echo';
var APIUrl = urljoin(baseURL, 'myworkflow', scriptFilepath, '_', wfTarget);
var requestOptions = {
headers : {
"Authorization" : base64Auth
}
}
console.log("APIUrl: " + APIUrl);
console.log("requestOptions: " + JSON.stringify(requestOptions));
request.get(APIUrl, requestOptions, function(error, response, body){
if (error) {
console.log(error);
res.status(500).send(error)
}else{
console.log(body);
res.json(body);
}
});
I tried running it on Bluemix and on my local env (with hard-coded VCAP_SERVICES). The response body is an HTML page with a title: "Redirect To OP"
Kindly please help

How to consume MTOM SOAP web service in node.js?

I need to download or process a file from a soap based web service in node.js.
can someone suggest me on how to handle this in node.js
I tried with 'node-soap' or 'soap' NPM module. it worked for normal soap web service. But, not for binary steam or MTOM based SOAP web service
I want to try to answer this... It's quite interesting that 2 years and 2 months later I can not figure it out how to easily solve the same problem.
I'm trying to get the attachment from a response like:
...
headers: { 'cache-control': 'no-cache="set-cookie"',
'content-type': 'multipart/related;boundary="----=_Part_61_425861994.1525782562904";type="application/xop+xml";start="";start-info="text/xml"',
...
body: '------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/xop+xml; charset=utf-8;
type="text/xml"\r\nContent-Transfer-Encoding: 8bit\r\nContent-ID:
\r\n\r\n....\r\n------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/octet-stream\r\nContent-Transfer-Encoding:
binary\r\nContent-ID:
\r\n\r\n�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0002,\u0000\u0000\u0005�\b\u0006\u0........binary....
I tried ws.js but no solution.
My solution:
var request = require("request");
var bsplit = require('buffer-split')
// it will extract "----=_Part_61_425861994.1525782562904" from the response
function getBoundaryFromResponse(response) {
var contentType = response.headers['content-type']
if (contentType && contentType.indexOf('boundary=') != -1 ) {
return contentType.split(';')[1].replace('boundary=','').slice(1, -1)
}
return null
}
function splitBufferWithPattern(binaryData, boundary) {
var b = new Buffer(binaryData),
delim = new Buffer(boundary),
result = bsplit(b, delim);
return result
}
var options = {
method: 'POST',
url: 'http://bla.blabal.../file',
gzip: true,
headers: {
SOAPAction: 'downloadFile',
'Content-Type': 'text/xml;charset=UTF-8'
},
body: '<soapenv: ... xml request of the file ... elope>'
};
var data = [];
var buffer = null;
var filename = "test.png"
request(options, function (error, response, body) {
if (error) throw new Error(error);
if (filename && buffer) {
console.log("filename: " + filename)
console.log(buffer.toString('base64'))
// after this, we can save the file from base64 ...
}
})
.on('data', function (chunk) {
data.push(chunk)
})
.on('end', function () {
var onlyPayload = splitBufferWithPattern(Buffer.concat(data), '\r\n\r\n') // this will get from PNG
buffer = onlyPayload[2]
buffer = splitBufferWithPattern(buffer, '\r\n-')[0]
console.log('Downloaded.');
})
I am not sure it will works in most of the cases. It looks like unstable code to my eyes and so I'm looking for something better.
Use ws.js
Here is how to fetch the file attachments:
const ws = require('ws.js')
const { Http, Mtom } = ws
var handlers = [ new Mtom(), new Http()];
var request = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
'<s:Body>' +
'<EchoFiles xmlns="http://tempuri.org/">' +
'<File1 />' +
'</EchoFiles>' +
'</s:Body>' +
'</s:Envelope>'
var ctx = { request: request
, contentType: "application/soap+xml"
, url: "http://localhost:7171/Service/mtom"
, action: "http://tempuri.org/IService/EchoFiles"
}
ws.send(handlers, ctx, function(ctx) {
//read an attachment from the soap response
var file = ws.getAttachment(ctx, "response", "//*[local-name(.)='File1']")
// work with the file
fs.writeFileSync("result.jpg", file)
})
Two limitations:
No basic auth provided out-of-box, patch required https://github.com/yaronn/ws.js/pull/40
If the file name is an url, you need to apply another patch at mtom.js. Replace:
.
xpath = "//*[#href='cid:" + encodeURIComponent(id) + "']//parent::*"
with:
xpath = "//*[#href='cid:" + id + "']//parent::*"

Can't upload file from Angularjs to Expressjs

I'm trying to upload a image from a AngularJS interface to a nodejs server (expressjs).
(I'm using mean.io)
Every time I upload someting, req.body logs "{}" and req.files logs "undefined"
I'm using angular-file-upload directive in AngularJS
Client-side code:
$scope.onFileSelect = function() {
console.log($files);
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
$scope.upload = $upload.upload({
url: 'map/set',
method: 'POST',
headers: {'enctype': 'multipart/form-data'},
data: {myObj: $scope.myModelObj},
file: file,
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
};
Server-side code
var app = express();
require(appPath + '/server/config/express')(app, passport, db);
app.use(bodyParser({uploadDir:'./uploads'}));
app.post('/map/set', function(req, res) {
console.log(req.body);
console.log(req.files);
res.end('Success');
});
*****Edit*****
HTML Code
<div class="row">
<input id="file" type="file" ng-file-select="onFileSelect()" >
</div>
Hand built request
$scope.onFileSelect = function() {
//$files: an array of files selected, each file has name, size, and type.
//console.log($files);
var xhr = new XMLHttpRequest();
// not yet supported in most browsers, some examples use
// this but it's not safe.
// var fd = document.getElementById('upload').getFormData();
var fd = new FormData();
var files = document.getElementById('myfileinput').files;
console.log(files);
for(var i = 0;i<files.length; i++) {
fd.append("file", files[i]);
}
/* event listeners */
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("abort", uploadCanceled, false);
function uploadComplete(){
console.log("complete");
}
function uploadProgress(){
console.log("progress");
}
function uploadFailed(){
console.log("failed");
}
function uploadCanceled(){
console.log("canceled");
}
xhr.open("POST", "map/set");
xhr.send(fd);
};
The latest version of mean.io uncluding express 4.x as dependency. In the documentation for migration express 3 to 4 you can read, express will no longer user the connect middlewares. Read more about here: https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x
The new body-parser module only handles urlencoded and json bodies. That means for multipart bodies (file uploads) you need an additional module like busboy or formadible.
Here is an example how I use angular-file-upload with busboy:
The AngularJS Stuff:
$upload.upload({
url: '/api/office/imageUpload',
data: {},
file: $scope.$files
}) …
I write a little helper module to handle uploads with busboy easier. It’s not very clean coded, but do the work:
var env = process.env.NODE_ENV || 'development';
var Busboy = require('busboy'),
os = require('os'),
path = require('path'),
config = require('../config/config')[env],
fs = require('fs');
// TODO: implement file size limit
exports.processFileUpload = function(req, allowedExtensions, callback){
var busboy = new Busboy({ headers: req.headers });
var tempFile = '';
var fileExtenstion = '';
var formPayload = {};
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
fileExtenstion = path.extname(filename).toLowerCase();
tempFile = path.join(os.tmpDir(), path.basename(fieldname)+fileExtenstion);
file.pipe(fs.createWriteStream(tempFile));
});
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
var jsonValue = '';
try {
jsonValue = JSON.parse(val);
} catch (e) {
jsonValue = val;
}
formPayload[fieldname] = jsonValue;
});
busboy.on('finish', function() {
if(allowedExtensions.length > 0){
if(allowedExtensions.indexOf(fileExtenstion) == -1) {
callback({message: 'extension_not_allowed'}, tempFile, formPayload);
} else {
callback(null, tempFile, formPayload)
}
} else {
callback(null, tempFile, formPayload)
}
});
return req.pipe(busboy);
}
In my controller i can use the module that way:
var uploader = require('../helper/uploader'),
path = require('path');
exports.uploadEmployeeImage = function(req,res){
uploader.processFileUpload(req, ['.jpg', '.jpeg', '.png'], function(uploadError, tempPath, formPayload){
var fileExtenstion = path.extname(tempPath).toLowerCase();
var targetPath = "/exampleUploadDir/testFile" + fileExtenstion;
fs.rename(tempPath, targetPath, function(error) {
if(error){
return callback("cant upload employee image");
}
callback(null, newFileName);
});
});
}
I'm going to take a guess here that the header settings are incorrect.
headers: {'enctype': 'multipart/form-data'},
Should be changed to:
headers: {'Content-Type': 'multipart/form-data'},
Ensure you have an 'id' AND 'name' attribute on the file input - not having an id attribute can cause problems on some browsers. Also, try building the request like this:
var xhr = new XMLHttpRequest();
// not yet supported in most browsers, some examples use
// this but it's not safe.
// var fd = document.getElementById('upload').getFormData();
var fd = new FormData();
var files = document.getElementById('myfileinput').files;
for(var i = 0;i<files.length; i++) {
fd.append("file", files[i]);
}
/* event listeners */
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "your/url");
xhr.send(fd);
angular isn't great with file uploads so doing it by hand might help.

MEAN Stack File uploads

I've recently started programming with the MEAN Stack, and I'm currently implementing some sort of social network. Been using the MEAN.io framework to do so.
My main problem right now is getting the file upload to work, because what I want to do is receive the file from the form into the AngularJS Controller and pass it along with more info's to ExpressJS so I can finally send everything to MongoDB. (I'm building a register new user form).
I dont want to store the file itself on the database but I want to store a link to it.
I've searched dozens of pages on google with different search queries but I couldn't find anything that I could understand or worked. Been searching for hours to no result. That's why I've came here.
Can anyone help me with this?
Thanks :)
EDIT: Maybe a bit of the code would help understand.
The default MEAN.io Users Angular controller which I'm using as foundation has this:
$scope.register = function(){
$scope.usernameError = null;
$scope.registerError = null;
$http.post('/register', {
email: $scope.user.email,
password: $scope.user.password,
confirmPassword: $scope.user.confirmPassword,
username: $scope.user.username,
name: $scope.user.fullname
})//... has a bit more code but I cut it because the post is the main thing here.
};
What I want to do is:
Receive a file from a form, onto this controller and pass it along with email, password, name, etc, etc and be able to use the json on expressjs, which sits on the server side.
The '/register' is a nodejs route so a server controller which creates the user (with the user schema) and sends it to the MongoDB.
I recently did something just like this. I used angular-file-upload. You'll also want node-multiparty for your endpoint to parse the form data. Then you could use s3 for uploading the file to s3.
Here's some of my [edited] code.
Angular Template
<button>
Upload <input type="file" ng-file-select="onFileSelect($files)">
</button>
Angular Controller
$scope.onFileSelect = function(image) {
$scope.uploadInProgress = true;
$scope.uploadProgress = 0;
if (angular.isArray(image)) {
image = image[0];
}
$scope.upload = $upload.upload({
url: '/api/v1/upload/image',
method: 'POST',
data: {
type: 'profile'
},
file: image
}).progress(function(event) {
$scope.uploadProgress = Math.floor(event.loaded / event.total);
$scope.$apply();
}).success(function(data, status, headers, config) {
AlertService.success('Photo uploaded!');
}).error(function(err) {
$scope.uploadInProgress = false;
AlertService.error('Error uploading file: ' + err.message || err);
});
};
Route
var uuid = require('uuid'); // https://github.com/defunctzombie/node-uuid
var multiparty = require('multiparty'); // https://github.com/andrewrk/node-multiparty
var s3 = require('s3'); // https://github.com/andrewrk/node-s3-client
var s3Client = s3.createClient({
key: '<your_key>',
secret: '<your_secret>',
bucket: '<your_bucket>'
});
module.exports = function(app) {
app.post('/api/v1/upload/image', function(req, res) {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
var file = files.file[0];
var contentType = file.headers['content-type'];
var extension = file.path.substring(file.path.lastIndexOf('.'));
var destPath = '/' + user.id + '/profile' + '/' + uuid.v4() + extension;
var headers = {
'x-amz-acl': 'public-read',
'Content-Length': file.size,
'Content-Type': contentType
};
var uploader = s3Client.upload(file.path, destPath, headers);
uploader.on('error', function(err) {
//TODO handle this
});
uploader.on('end', function(url) {
//TODO do something with the url
console.log('file opened:', url);
});
});
});
}
I changed this from my code, so it may not work out of the box, but hopefully it's helpful!
Recently a new package was added to the list of packages on mean.io. It's a beauty!
Simply run:
$ mean install mean-upload
This installs the package into the node folder but you have access to the directives in your packages.
http://mean.io/#!/packages/53ccd40e56eac633a3eee335
On your form view, add something like this:
<div class="form-group">
<label class="control-label">Images</label>
<mean-upload file-dest="'/packages/photos/'" upload-file-callback="uploadFileArticleCallback(file)"></mean-upload>
<br>
<ul class="list-group">
<li ng-repeat="image in article.images" class="list-group-item">
{{image.name}}
<span class="glyphicon glyphicon-remove-circle pull-right" ng-click="deletePhoto(image)"></span>
</li>
</ul>
</div>
And in your controller:
$scope.uploadFileArticleCallback = function(file) {
if (file.type.indexOf('image') !== -1){
$scope.article.images.push({
'size': file.size,
'type': file.type,
'name': file.name,
'src': file.src
});
}
else{
$scope.article.files.push({
'size': file.size,
'type': file.type,
'name': file.name,
'src': file.src
});
}
};
$scope.deletePhoto = function(photo) {
var index = $scope.article.images.indexOf(photo);
$scope.article.images.splice(index, 1);
}
Enjoy!
Mean-upload has been obsoleted and is now called "upload". It is managed in - https://git.mean.io/orit/upload
I know this post is old. I came across it and #kentcdodds had an answer that i really liked, but the libraries he used are now out of date and I could not get them to work. So after some research i have a newer similar solution I want to share.
HTML using ng-upload
<form >
<div style="margin-bottom: 15px;">
<button type="file" name="file" id="file" ngf-select="uploadFiles($file, $invalidFiles)" accept="image/*" ngf-max-height="1000" ngf-max-size="1MB">Select File</button>
</div>
</form>
INCLUDE ng-upload module
download it, references the files and include the module
var app = angular.module('app', ['ngFileUpload']);
this will give you access to the Upload service.
Controller code
$scope.uploadFiles = function(file, errFiles) {
$scope.f = file;
$scope.errFile = errFiles && errFiles[0];
if (file) {
file.upload = Upload.upload({
url: 'you-api-endpoint',
data: {file: file}
});
//put promise and event watchers here if wanted
}
};
NODE api code
All the code below is in a separate route file which is required in my main server.js file.
require('./app/app-routes.js')(app, _);
var fs = require('fs');
var uuid = require('uuid');
var s3 = require('s3fs'); // https://github.com/RiptideElements/s3fs
var s3Impl = new s3('bucketname', {
accessKeyId: '<your access key id>',
secretAccessKey: '< your secret access key >'
});
var multiparty = require('connect-multiparty');
var multipartyMiddleware = multiparty();
module.exports = function(app, _) {
app.use(multipartyMiddleware);
app.post('/your-api-endpoint',function(req, res){
var file = req.files.file; // multiparty is what allows the file to to be accessed in the req
var stream = fs.createReadStream(file.path);
var extension = file.path.substring(file.path.lastIndexOf('.'));
var destPath = '/' + req.user._id + '/avatar/' + uuid.v4() + extension;
var base = 'https://you-bucket-url';
return s3Impl.writeFile(destPath, stream, {ContentType: file.type}).then(function(one){
fs.unlink(file.path);
res.send(base + destPath);
});
});
All i was trying to do was upload a unique avatar for a user. Hope this helps!!

Resources