Uncompress gzipped http request body to json in Node.js - node.js

I have a windows 8 application connecting to a web service written in Node.js. On the windows 8 side I compressed my request body to gzip. But on the Node.js side I found that my req.body type was Object.
I cannot use zlib to uncomporess the body since it's not a stream.
I can use zlib to uncomporess the req, but I don't know how to retrieve the req.body content from the unzipped stream and parse the body in JSON format.
BTW, I reviewed my request through Fiddler and it told me the request body was gzipped, and I can see my raw body through Fiddler after unzipped so the request should be correct.
Updated
Below is my Node.js app
(function () {
var express = require("express");
var zlib = require("zlib");
var app = express();
var port = 12345;
app.configure(function () {
app.use(express.compress());
app.use(express.bodyParser());
});
app.post("/test", function (req, res) {
var request = req.body;
req.pipe(zlib.createGunzip());
var response = {
status: 0,
value: "OK"
};
res.send(200, response);
});
console.log("started at port %d", port);
app.listen(port);
})();
And below is my windows store app code (partial)
private async void button1_Click_1(object sender, RoutedEventArgs e)
{
var message = new
{
Name = "Shaun",
Value = "12345678901234567890123456789012345678901234567890"
};
var json = await JsonConvert.SerializeObjectAsync(message, Formatting.Indented);
var bytes = Encoding.UTF8.GetBytes(json);
var client = new HttpClient();
client.BaseAddress = new Uri("http://192.168.56.1:12345/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.ExpectContinue = false;
var jsonContent = new JsonContent(message);
var gzipContent = new GZipContent3(jsonContent);
var res = await client.PostAsync("test", gzipContent);
var dialog = new Windows.UI.Popups.MessageDialog(":)", "完成");
await dialog.ShowAsync();
}
internal class GZipContent3 : ByteArrayContent
{
public GZipContent3(HttpContent content)
: base(LoadGZipBytes(content))
{
//base.Headers.ContentType = content.Headers.ContentType;
base.Headers.ContentType = new MediaTypeHeaderValue("x-application/x-gzip");
base.Headers.ContentEncoding.Add("gzip");
}
private static byte[] LoadGZipBytes(HttpContent content)
{
var source = content.ReadAsByteArrayAsync().Result;
byte[] buffer;
using (var outStream = new MemoryStream())
{
using (var gzip = new GZipStream(outStream, CompressionMode.Compress, true))
{
gzip.Write(source, 0, source.Length);
}
buffer = outStream.ToArray();
}
return buffer;
}
}
internal class JsonContent : StringContent
{
private const string defaultMediaType = "application/json";
public JsonContent(string json)
: base(json)
{
var mediaTypeHeaderValue = new MediaTypeHeaderValue(defaultMediaType);
mediaTypeHeaderValue.CharSet = Encoding.UTF8.WebName;
base.Headers.ContentType = mediaTypeHeaderValue;
}
public JsonContent(object content)
: this(GetJson(content))
{
}
private static string GetJson(object content)
{
if (content == null)
{
throw new ArgumentNullException("content");
}
var json = JsonConvert.SerializeObject(content, Formatting.Indented);
return json;
}
}

http://www.senchalabs.org/connect/json.html. Basically you need to write your own middleware based on connect.json() that pipes through an uncompression stream like connect.compress() but the opposite direction: http://www.senchalabs.org/connect/compress.html
Also, make sure you're sending the correct Content-Encoding header in your request.
If you show me what you have so far I can help you further.

I was working on similar thing and finally landed on
function getGZipped(req, callback) {
var gunzip = zlib.createGunzip();
req.pipe(gunzip);
var buffer = [];
gunzip.on('data', function (data) {
// decompression chunk ready, add it to the buffer
buffer.push(data);
}).on('end', function () {
//response and decompression complete, join the buffer and return
callback(null, JSON.parse(buffer));
}).on('error', function (e) {
callback(e);
});
}

Related

Node JS Soap to send a file to sharepoint based webserver using CopyIntoItems

I am writing a Node JS SOAP client using Node-Soap module to send a file to a remote SharePoint based Web Services.
The machine client requires a proxy to access Internet and the SharePoint WS requires an account (user, pwd). Below are the code source.
However, I always have the error "(node:20857) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Cannot parse response".
Someone can help me, please?
var process = require('process');
var fs = require('fs');
var request = require('request')
var soap = require('soap');
var apiWSDL = '.../test-collab/WS/_vti_bin/copy.asmx?wsdl';
function sendFile() {
var p = new Promise(function (resolve, reject) {
request_with_defaults = request.defaults({
'proxy': 'http://***:***#10.115.108.109:8080',
'timeout': 50000,
'connection': 'keep-alive'
});
var options = {
'request': request_with_defaults,
endpoint: 'https://.../test-collab/WS/_vti_bin/copy.asmx',
}
var byteArray = fs.readFileSync('test.txt').toString('base64');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
//process.env.https_proxy = 'http://***#***:10.115.108.109:8080';
soap.createClient(apiWSDL, options, function (err, client) {
if (err) throw new Error(err);
var args = {
DestinationUrls: 'https://.../test-collab/WS/CAS/test.txt',
Stream: byteArray
}
client.setSecurity(new soap.ClientSSLSecurity(null, null, null, { /*default request options like */
strictSSL: false,
rejectUnauthorized: false,
// hostname: 'some-hostname'
//secureOptions: constants.SSL_OP_NO_TLSv1_2,
forever: true,
}));
client.addHttpHeader('vm6_webapp', 'SERVICE');
client.addHttpHeader('vm6_password', '***');
client.addHttpHeader('vm6_user', '***');
client.CopyIntoItems(args, function (err, result) {
//console.log(err);
if (err) {
console.log(err);
reject(err);
}
var sets;
try {
console.log(result);
if (result.length) {
resolve(result);
} else {
reject(result);
}
} catch (error) {
console.log("error");
reject("error und")
}
});
});
});
return p;
}
As the error message is Cannot parse response, two possibilities can happen:
either the response is not in XML format
or the XML response is not acceptable by SOAP response message defined in wsdl.
Can you redo the SOAP request by using an existing client, such as, SoapUI to confirm?
Otherwise, I propose to use console.error( err.stack ) rather than console.log( err ) to get the full execution trace of err.
Thank Nghia for your reply.
In fact, I've written a SOAP client in Java for this WebServers before and it works. That means the paramters are ok as well as the XML response is ok.
Here are the code in Java:
MoccaClient clientWSMocca = new MoccaClient();
CopySoap copySoap = (CopySoap)clientWSMocca.getClient(site_soap_url,
proxy_host, proxy_port, proxy_user, proxy_password,
mocca_user, mocca_password, mocca_web_app,
CopySoap.class);
// Récupération sous forme de tableau de bytes du fichier
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
InputStream in = new BufferedInputStream(new FileInputStream(file_path));
BufferedOutputStream bufOut = new BufferedOutputStream(out);
for (int b = in.read(); b != -1; b = in.read()) {
bufOut.write(b);
}
in.close();
bufOut.close();
} catch (Exception e) {
e.printStackTrace();
}
// Initialisation des variables de contexte
FieldInformation fieldInformation = new FieldInformation();
String destinationUrl = site_url + file_name;
DestinationUrlCollection destinationUrlCollection = new DestinationUrlCollection();
destinationUrlCollection.getString().add(destinationUrl);
FieldInformationCollection fieldInformationCollection = new FieldInformationCollection();
fieldInformationCollection.getFieldInformation().add(fieldInformation);
Holder<CopyResultCollection> copyResult= new Holder<CopyResultCollection>();
Holder<Long> getItemResult = new Holder<Long>();
copySoap.copyIntoItems(file_path, destinationUrlCollection, fieldInformationCollection, out.toByteArray(), getItemResult, copyResult);
MoccaClient.java
public class MoccaClient {
public Object getClient(String url,
String proxyhost, int proxyport, String userproxy, String passwordproxy,
String moccauser, String moccapassword, String moccawebapp,
Class<?> serviceclass) {
System.setProperty("org.apache.cxf.JDKBugHacks.defaultUsesCaches", "true");
boolean bssl = false;
if (url.startsWith("https")) {
bssl = true;
}
if (url.startsWith("HTTPS")) {
bssl = true;
}
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.getInInterceptors().add(new MyInterceptor());
factory.setServiceClass(serviceclass);
factory.setAddress(url);
Object client = factory.create();
Client clientDuProxy = ClientProxy.getClient(client);
Map<String, List<String>> headers = new HashMap();
headers.put("vm6_user", Arrays.asList(moccauser));
headers.put("vm6_password", Arrays.asList(moccapassword));
headers.put("vm6_webapp", Arrays.asList(moccawebapp));
clientDuProxy.getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
HTTPConduit http = (HTTPConduit)clientDuProxy.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
http.setClient(httpClientPolicy);
if (proxyhost != null) {
http.getClient().setProxyServer(proxyhost);
http.getClient().setProxyServerPort(proxyport);
}
if (userproxy != null) {
http.getProxyAuthorization().setUserName(userproxy);
http.getProxyAuthorization().setPassword(passwordproxy);
}
if (bssl) {
TrustManager[] trustCerts = new TrustManager[]{new AllTrust()};
TLSClientParameters tcp = new TLSClientParameters();
tcp.setTrustManagers(trustCerts);
tcp.setSecureSocketProtocol("TLS");
tcp.setDisableCNCheck(true);
http.setTlsClientParameters(tcp);
}
return client;
}
}

How can I get and modify response.body in nodejs?

I am using express and write this code in a middleware. I need to proxy a request to another newUrl and have to get the body of the response from newUrl. But I don't know how to get it in this way. Can anybody tell me how to get the res.body?
var stream = req.pipe(request(newUrl)).pipe(res);
stream.on('finish', function() {
// how can I get res.body from the newUrl?
next();
});
Here is an easy way on how to make proxy request using streams.
'use strict';
const
stream = require('stream'),
util = require('util'),
http = require('http');
let Transform = stream.Transform;
// make Trasform stream
function MyProxyStream(options) {
if (!(this instanceof MyProxyStream)) {
return new MyProxyStream(options);
}
// set proxy url
this.proxyUrl = 'http://go-to-proxy'
Transform.call(this, options);
}
util.inherits(MyProxyStream, Transform);
// Transform stuff here
MyProxyStream.prototype._transform = function (chunk, enc, cb) {
// send proxy request somethere -> get data
// chunk is string cast to Object with JSON.parse(chunk)
// request.post(this.proxyUrl, chunk)
let data = "my proxy data";
this.push(data);
return cb();
};
const server = http.createServer((req, res) => {
let transformer = new MyProxyStream({objectMode: true});
req.setEncoding('utf8');
req.pipe(transformer).pipe(res);
});
server.listen(1337);
Hope this helps.

How to get binary data from ng-file-upload file object?

I'm attempting to use the ng-file-upload directive to provide file upload functionality in my angular app.
I've got it working for the most part - I can select multiple files and loop through to grab the file name and file types. I just can't seem to figure out where the actual binary data of each file is stored in the file object.
I tried using the approach outlined in this post - AngularJS Upload a file and send it to a DB, but that results in a an error that "$q is not defined".
function create_blob(file) {
var deferred = $q.defer();
var reader = new FileReader();
reader.onload = function () {
deferred.resolve(reader.result);
};
reader.readAsDataURL(file);
return deferred.promise;
}
So then I tried the approach outlined in this post - Send an uploaded image to the server and save it in the server, but again I'm running into an error reading "dataURI.split is not a function".
function dataURItoBlob(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: mimeString
});
}
The code I'm using is as follows:
function create_blob(file) {
var deferred = $q.defer();
var reader = new FileReader();
reader.onload = function () {
deferred.resolve(reader.result);
};
reader.readAsDataURL(file);
return deferred.promise;
}
function dataURItoBlob(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: mimeString
});
}
$scope.uploadFiles = function (files) {
$scope.files = files;
angular.forEach(files, function (file) {
if (file && !file.$error) {
//var reader = new FileReader();
//console.log(reader.readAsDataURL(file));
//var binary = create_blob(file);
var fileBinary = dataURItoBlob(file);
$http({
url: root + '/DesktopModules/ServiceProxy/API/NetSuite/InsertCaseFile',
method: "POST",
//headers: { 'caseId': id, 'fileName': file.name, fileContent: $.base64.encode(file) }
headers: { 'caseId': id, 'fileName': file.name, fileContent: fileBinary }
}).
success(function (data, status, headers, config) {
//if (data == true) {
// getCase();
// $scope.newMessage = "";
// //toaster.pop('success', "", "Message succesfully submitted.",0);
//}
}).
error(function (data, status, headers, config) {
});
file.upload.progress(function (evt) {
file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
});
}
});
}
What am I overlooking?
It depends on what format your DB is accepting for file upload. If it support multipart form data, then you can just use
Upload.upload({file: file, url: my/db/url}).then(...);
if it accepts post requests with file's binary as content of the request (like CouchDB, imgur, ...) then you can do
Upload.http({data: file, url: my/db/url, headers: {'Content-Type': file.type}})...;
if you db just accept json objects and you want to store the file as base64 data url in the database like this question then you can do
Upload.dataUrl(file, true).then(function(dataUrl) {
$http.post(url, {
fileBase64DataUrl: dataUrl,
fileName: file.name,
id: uniqueId
});
})

Send PDF file from AngularJS to NodeJS

i need to send a PDF file from angularjs client to NodeJS service.
I did the angularjs service, and when i receive the file its a string like this:
%PDF-1.3
3 0 obj
<</Type /Page
/Parent 1 0 R
/Reso
How can i reconvert this string to PDF in NodeJS?
This is the client code:
var sendByEmail = function () {
$scope.generatingPdf = true;
$('#budget').show();
var pdf = new JsPDF('p', 'pt', 'letter');
var source = $('#budget')[0];
pdf.addHTML(source, 0, 0, function () {
var resultPdf = pdf.output();
BillService.sendByEmail("rbrlnx#gmail.com", resultPdf).then(function () {
});
$('#budget').hide();
});
};
var sendByEmail = function (email, file) {
var deferred = $q.defer();
var data = {
email: email,
file: file
};
BillService.sendByEmail(data, function (result) {
deferred.resolve(result);
}, function () {
deferred.reject();
});
return deferred.promise;
};
The server code controller its empty:
var sendByEmail = function (req, res, next) {
var file = req.body.file;
};
I experimented with this a while ago, and I came up with this. It's not production ready by a long shot maybe you find it useful. It's free of front end libraries (except Angular ofcourse), but assumes you're using Express 4x and body-parser.
The result:
In the browser:
On the server:
What you're seeing:
You're seeing a tiny node server, serving static index.html and angular files, and a POST route receiving a PDF in base64 as delivered by the HTML FileReader API, and saves it to disk.
Instead of saving to disk, you can send it as an email attachment. See for instance here or here for some info on that.
The example below assumes uploading a PDF by a user through a file input, but the idea is the same for all other ways of sending a document to your back end system. The most important thing is to send the pdf data as BASE64, because this is the format that most file writers and email packages use (as opposed to straight up binary for instance..). This also goes for images, documents etc.
How did I do that:
In your HTML:
<div pdfs>Your browser doesn't support File API.</div>
A directive called pdfs:
myApp.directive('pdfs', ['upload', function(upload) {
return {
replace: true,
scope: function() {
files = null;
},
template: '<input id="files" type="file">',
link: function(scope,element) {
element.bind('change', function(evt) {
scope.$apply(function() {
scope.files = evt.target.files;
});
});
},
controller: function($scope, $attrs) {
$scope.$watch('files', function(files) {
//upload.put(files)
if(typeof files !== 'undefined' && files.length > 0) {
for(var i = 0; i<files.length;i++) {
readFile(files[i])
}
}
}, true);
function readFile(file) {
var reader = new FileReader();
reader.addEventListener("loadend", function(evt) {
upload.post({name: file.name, data: reader.result})
})
if(reader.type = 'application/pdf') {
reader.readAsDataURL(file);
}
}
}
}
}]);
A tiny service:
myApp.service('upload', function($http) {
this.post = function(file) {
$http.post('/pdf', file);
}
});
And a node server:
var express = require('express');
var bodyParser = require('body-parser')
var fs = require("fs");
var app = express();
app.use(express.static('.'));
app.use( bodyParser.json({limit: '1mb'}) );
app.post('/pdf', function(req, res){
var name = req.body.name;
var pdf = req.body.data;
var pdf = pdf.replace('data:application/pdf;base64,', '');
res.send('received');
fs.writeFile(name, pdf, 'base64', function(err) {
console.log(err);
});
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});

send params to node server using cordova

I'm trying to upload file and send params in the same request it's possible with filetransfer but i have a problem in the server side the req.body is always empty i'm using formidable module
this is the client side
upload = function (imageURI) {
var ft = new FileTransfer(),
options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = 'filename.jpg'; // We will use the name auto-generated by Node at the server side.
options.mimeType = "image/jpeg";
options.chunkedMode = false;
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
alert(imageURI);
ft.upload(imageURI, serverURL + "/upload",
function (e) {
getFeed();
},
function (e) {
alert("Upload failed");
}, options);
},
this is the server side
var form = new formidable.IncomingForm();
form.parse(req, function(error, fields, files) {
console.log(req.body.value1);
console.log("Traitement terminé");
i found the problem i had to replace
console.log(req.body.value1);
by
console.log(fields.value1);

Resources