Apple Fairplay TVML can't get AssetId, certificate and key - ios

After spending many days on search on google about how to use Fairplay in TVML (Apple TV), i can't get this work.
I am trying to use Apple FairPlay using TVML, there is no clear documentation or example that can help for integration.
in my code on singleVideo.loadCertificate function i am trying to get the assetId, but it's keep calling the same function with out errors
singleVideo.loadAssetID = function(url, callback) {
console.log("===== HERE === ");
console.log("======== loadAssetID=======");
console.log(url);
var extractContentId = function (initData) {
var uri = initData;
var uriParts = uri.split('://');
var protocol = uriParts[0].slice(-3);
var contentId = uriParts.length > 0 ? uriParts[1] : '';
return protocol.toLowerCase() == 'skd' ? contentId : '';
};
asset=extractContentId(url);
var error=null;
console.log("->AssetID="+ asset);
callback(btoa(asset));
}
singleVideo.loadCertificate = function(url, callback) {
console.log("==== Getting the certificate");
var certUrl = "cer url";
var request = new XMLHttpRequest();
request.responseType = 'arraybuffer';
request.addEventListener('load', function () {
console.log( "RESPONSE ");
console.log( request.getAllResponseHeaders () );
console.log( request.response );
var base64 = btoa(new Uint8Array(request.response)
.reduce((data, byte) => data + String.fromCharCode(byte), '')
);
callback( base64);
}, false);
request.addEventListener('error', function (error) {
console.log(error)}
, false);
request.open('GET', certUrl, true);
request.setRequestHeader('Pragma', 'Cache-Control: no-cache');
request.setRequestHeader("Cache-Control", "max-age=0");
request.send();
}
singleVideo.loadKey = function(url, requestData, callback) {
console.log("======= [FPS] getting the FPS asset key");
var assetID = asset;
var postBody = "payload=" + base64EncodeUint8Array(requestData) + "&id=" + base64EncodeUint8Array(assetID);
var keyUrl = "server key url";
console.log("======= [FPS] keyurl: " + keyUrl + " sending " + postBody);
var request = new XMLHttpRequest();
request.responseType = 'text';
request.addEventListener('load', function(){
console.log(request);
callback(request.responseText, null, null);
}, false);
request.addEventListener('error', function(){
console.log(request);
callback(null, null, "Got bad response from server: " + request.status)
}, false);
request.open('POST', keyUrl, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.send(postBody);
}
can anyone help me with integration and tell me where is the error in my code?

Related

Netsuite OAuth Not Working

I've tried implementing Netsuite's OAuth Example, as illustrated here: https://netsuite.custhelp.com/app/answers/detail/a_id/42165. I've posted it directly below so you don't have to go to the page if you don't want.
Unfortunately, it's not working. I know that I have the correct token and consumer key's and secrets, and the correct account ID. It's giving me a nice error though:
{"error" : {"code" : "INVALID_LOGIN_ATTEMPT", "message" : "Invalid login attempt."}}
I can look in my login audits, and see that it's saying that the signature is invalid. But the code itself looks fine and was provided by Netsuite.
I've also tried some approaches in Node.JS and haven't gotten them working. Any suggestions as to which direction I should go next?
import oauth2 as oauth
import requests
import time
url = "https://rest.netsuite.com/app/site/hosting/restlet.nl?script=992&deploy=1"
token = oauth.Token(key="080eefeb395df81902e18305540a97b5b3524b251772adf769f06e6f0d9dfde5", secret="451f28d17127a3dd427898c6b75546d30b5bd8c8d7e73e23028c497221196ae2")
consumer = oauth.Consumer(key="504ee7703e1871f22180441563ad9f01f3f18d67ecda580b0fae764ed7c4fd38", secret="b36d202caf62f889fbd8c306e633a5a1105c3767ba8fc15f2c8246c5f11e500c")
http_method = "GET"
realm="ACCT123456"
params = {
'oauth_version': "1.0",
'oauth_nonce': oauth.generate_nonce(),
'oauth_timestamp': str(int(time.time())),
'oauth_token': token.key,
'oauth_consumer_key': consumer.key
}
req = oauth.Request(method=http_method, url=url, parameters=params)
signature_method = oauth.SignatureMethod_HMAC_SHA1()
req.sign_request(signature_method, consumer, token)
header = req.to_header(realm)
headery = header['Authorization'].encode('ascii', 'ignore')
headerx = {"Authorization": headery, "Content-Type":"application/json"}
print(headerx)
conn = requests.get("https://rest.netsuite.com/app/site/hosting/restlet.nl?script=992&deploy=1",headers=headerx)
print(conn.text)
I've also implemented a few Node.JS samples (that haven't worked either). Here is one of them (CryptoJS HMAC-SHA1 and HMAC-SHA256 are on top, then the oauth-1.0a library, and then the code provided by Netsuite with a few small changes to make it work (added a hash_function, renamed 'public' to 'key'):
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2)b[f>>>3]|=parseInt(a.substr(f,
2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}},
r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a,
d)).finalize(b)}}});var s=e.algo={};return e}(Math);
(function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^
g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})();
(function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++)n[h]^=1549556828,j[h]^=909522486;p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d=
this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})();
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e<a;e++)c[b+e>>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535<d.length)for(e=0;e<a;e+=4)c[b+e>>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d<a;d+=4)c.push(4294967296*h.random()|0);return new r.init(c,a)}}),l=f.enc={},k=l.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++){var e=c[b>>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b+=2)d[b>>>3]|=parseInt(a.substr(b,
2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b<a;b++)d.push(String.fromCharCode(c[b>>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b<c;b++)d[b>>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}},
u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);c.sigBytes-=b}return new r.init(g,b)},clone:function(){var a=m.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});g.Hasher=u.extend({cfg:m.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){u.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,d){return(new a.init(d)).finalize(c)}},_createHmacHelper:function(a){return function(c,d){return(new t.HMAC.init(a,
d)).finalize(c)}}});var t=f.algo={};return f}(Math);
(function(h){for(var s=CryptoJS,f=s.lib,g=f.WordArray,q=f.Hasher,f=s.algo,m=[],r=[],l=function(a){return 4294967296*(a-(a|0))|0},k=2,n=0;64>n;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]=
c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math);
(function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j<h;j++)k[j]^=1549556828,n[j]^=909522486;r.sigBytes=l.sigBytes=m;this.reset()},reset:function(){var f=this._hasher;f.reset();f.update(this._iKey)},update:function(f){this._hasher.update(f);return this},finalize:function(f){var g=
this._hasher;f=g.finalize(f);g.reset();return g.finalize(this._oKey.clone().concat(f))}})})();
//oauth-1.0a
if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
module.exports = OAuth;
}
/**
* Constructor
* #param {Object} opts consumer key and secret
*/
function OAuth(opts) {
if(!(this instanceof OAuth)) {
return new OAuth(opts);
}
if(!opts) {
opts = {};
}
if(!opts.consumer) {
throw new Error('consumer option is required');
}
this.consumer = opts.consumer;
this.nonce_length = opts.nonce_length || 32;
this.version = opts.version || '1.0';
this.parameter_seperator = opts.parameter_seperator || ', ';
this.realm = opts.realm;
if(typeof opts.last_ampersand === 'undefined') {
this.last_ampersand = true;
} else {
this.last_ampersand = opts.last_ampersand;
}
// default signature_method is 'PLAINTEXT'
this.signature_method = opts.signature_method || 'PLAINTEXT';
if(this.signature_method == 'PLAINTEXT' && !opts.hash_function) {
opts.hash_function = function(base_string, key) {
return key;
}
}
if(!opts.hash_function) {
throw new Error('hash_function option is required');
}
this.hash_function = opts.hash_function;
this.body_hash_function = opts.body_hash_function || this.hash_function;
}
/**
* OAuth request authorize
* #param {Object} request data
* {
* method,
* url,
* data
* }
* #param {Object} key and secret token
* #return {Object} OAuth Authorized data
*/
OAuth.prototype.authorize = function(request, token) {
var oauth_data = {
oauth_consumer_key: this.consumer.key,
oauth_nonce: this.getNonce(),
oauth_signature_method: this.signature_method,
oauth_timestamp: this.getTimeStamp(),
oauth_version: this.version
};
if(!token) {
token = {};
}
if(token.key !== undefined) {
oauth_data.oauth_token = token.key;
}
if(!request.data) {
request.data = {};
}
if(request.includeBodyHash) {
oauth_data.oauth_body_hash = this.getBodyHash(request, token.secret)
}
oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);
return oauth_data;
};
/**
* Create a OAuth Signature
* #param {Object} request data
* #param {Object} token_secret key and secret token
* #param {Object} oauth_data OAuth data
* #return {String} Signature
*/
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
return this.hash_function(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};
/**
* Create a OAuth Body Hash
* #param {Object} request data
*/
OAuth.prototype.getBodyHash = function(request, token_secret) {
var body = typeof request.data === 'string' ? request.data : JSON.stringify(request.data)
if (!this.body_hash_function) {
throw new Error('body_hash_function option is required');
}
return this.body_hash_function(body, this.getSigningKey(token_secret))
};
/**
* Base String = Method + Base Url + ParameterString
* #param {Object} request data
* #param {Object} OAuth data
* #return {String} Base String
*/
OAuth.prototype.getBaseString = function(request, oauth_data) {
return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};
/**
* Get data from url
* -> merge with oauth data
* -> percent encode key & value
* -> sort
*
* #param {Object} request data
* #param {Object} OAuth data
* #return {Object} Parameter string data
*/
OAuth.prototype.getParameterString = function(request, oauth_data) {
var base_string_data;
if (oauth_data.oauth_body_hash) {
base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.deParamUrl(request.url))));
} else {
base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));
}
var data_str = '';
//base_string_data to string
for(var i = 0; i < base_string_data.length; i++) {
var key = base_string_data[i].key;
var value = base_string_data[i].value;
// check if the value is an array
// this means that this key has multiple values
if (value && Array.isArray(value)){
// sort the array first
value.sort();
var valString = "";
// serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2
value.forEach((function(item, i){
valString += key + '=' + item;
if (i < value.length){
valString += "&";
}
}).bind(this));
data_str += valString;
} else {
data_str += key + '=' + value + '&';
}
}
//remove the last character
data_str = data_str.substr(0, data_str.length - 1);
return data_str;
};
/**
* Create a Signing Key
* #param {String} token_secret Secret Token
* #return {String} Signing Key
*/
OAuth.prototype.getSigningKey = function(token_secret) {
token_secret = token_secret || '';
if(!this.last_ampersand && !token_secret) {
return this.percentEncode(this.consumer.secret);
}
return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};
/**
* Get base url
* #param {String} url
* #return {String}
*/
OAuth.prototype.getBaseUrl = function(url) {
return url.split('?')[0];
};
/**
* Get data from String
* #param {String} string
* #return {Object}
*/
OAuth.prototype.deParam = function(string) {
var arr = string.split('&');
var data = {};
for(var i = 0; i < arr.length; i++) {
var item = arr[i].split('=');
// '' value
item[1] = item[1] || '';
// check if the key already exists
// this can occur if the QS part of the url contains duplicate keys like this: ?formkey=formvalue1&formkey=formvalue2
if (data[item[0]]){
// the key exists already
if (!Array.isArray(data[item[0]])) {
// replace the value with an array containing the already present value
data[item[0]] = [data[item[0]]];
}
// and add the new found value to it
data[item[0]].push(decodeURIComponent(item[1]));
} else {
// it doesn't exist, just put the found value in the data object
data[item[0]] = decodeURIComponent(item[1]);
}
}
return data;
};
/**
* Get data from url
* #param {String} url
* #return {Object}
*/
OAuth.prototype.deParamUrl = function(url) {
var tmp = url.split('?');
if (tmp.length === 1)
return {};
return this.deParam(tmp[1]);
};
/**
* Percent Encode
* #param {String} str
* #return {String} percent encoded string
*/
OAuth.prototype.percentEncode = function(str) {
return encodeURIComponent(str)
.replace(/\!/g, "%21")
.replace(/\*/g, "%2A")
.replace(/\'/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29");
};
/**
* Percent Encode Object
* #param {Object} data
* #return {Object} percent encoded data
*/
OAuth.prototype.percentEncodeData = function(data) {
var result = {};
for(var key in data) {
var value = data[key];
// check if the value is an array
if (value && Array.isArray(value)){
var newValue = [];
// percentEncode every value
value.forEach((function(val){
newValue.push(this.percentEncode(val));
}).bind(this));
value = newValue;
} else {
value = this.percentEncode(value);
}
result[this.percentEncode(key)] = value;
}
return result;
};
/**
* Get OAuth data as Header
* #param {Object} oauth_data
* #return {String} Header data key - value
*/
OAuth.prototype.toHeader = function(oauth_data) {
var sorted = this.sortObject(oauth_data);
var header_value = 'OAuth ';
if (this.realm) {
header_value += 'realm="' + this.realm + '"' + this.parameter_seperator;
}
for(var i = 0; i < sorted.length; i++) {
if (sorted[i].key.indexOf('oauth_') !== 0)
continue;
header_value += this.percentEncode(sorted[i].key) + '="' + this.percentEncode(sorted[i].value) + '"' + this.parameter_seperator;
}
return {
Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
};
};
/**
* Create a random word characters string with input length
* #return {String} a random word characters string
*/
OAuth.prototype.getNonce = function() {
var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var result = '';
for(var i = 0; i < this.nonce_length; i++) {
result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
}
return result;
};
/**
* Get Current Unix TimeStamp
* #return {Int} current unix timestamp
*/
OAuth.prototype.getTimeStamp = function() {
return parseInt(new Date().getTime()/1000, 10);
};
////////////////////// HELPER FUNCTIONS //////////////////////
/**
* Merge object
* #param {Object} obj1
* #param {Object} obj2
* #return {Object}
*/
OAuth.prototype.mergeObject = function(obj1, obj2) {
obj1 = obj1 || {};
obj2 = obj2 || {};
var merged_obj = obj1;
for(var key in obj2) {
merged_obj[key] = obj2[key];
}
return merged_obj;
};
/**
* Sort object by key
* #param {Object} data
* #return {Array} sorted array
*/
OAuth.prototype.sortObject = function(data) {
var keys = Object.keys(data);
var result = [];
keys.sort();
for(var i = 0; i < keys.length; i++) {
var key = keys[i];
result.push({
key: key,
value: data[key],
});
}
return result;
};
//NETSUITE'S RESTLET
function callRESTlet(request, response) {
var remoteAccountID = 'ACCOUNT ID HERE';
var restletUrl = 'https://rest.netsuite.com/app/site/hosting/restlet.nl?script=992&deploy=1';
//user token
var token = {
key: 'ACCESS KEY HERE',
secret: 'ACCESS SECRET HERE'
};
//app credentials
var oauth = OAuth({
consumer: {
key: 'INTEGRATION KEY HERE',
secret: 'INTEGRATION SECRET HERE'
},
signature_method: 'HMAC-SHA1',
hash_function: function(base_string, key)
{
return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
}
});
var request_data = {
url: restletUrl,
method: 'GET',
data: {}
};
var oauth_data = {
oauth_consumer_key: oauth.consumer.key,
oauth_nonce: oauth.getNonce(),
oauth_signature_method: oauth.signature_method,
oauth_timestamp: oauth.getTimeStamp(),
oauth_version: '1.0',
oauth_token: token.key,
realm: remoteAccountID
};
var headerWithRealm = oauth.toHeader(oauth.authorize(request_data, token));
headerWithRealm.Authorization += ',realm="' + remoteAccountID + '"';
var restResponse = nlapiRequestURL(restletUrl, null, headerWithRealm, null, "GET");
var html = 'Calling: ' +
restletUrl +
'<br><br>' +
'Generated OAuth header:<br>' +
headerWithRealm.Authorization +
'<br><br>' +
'Response:<br>' +
restResponse.getBody()
response.write(html);
}
EDIT: Just published an npm module which should make things easier: https://www.npmjs.com/package/nsrestlet
Was able to get some code working after hunting through GitHub Code commits. Still, bknights response is really good.
Here's what I got working.
Assuming you have Node.js and npm installed, run:
npm install request
npm install oauth-1.0a#1.0.1
It's really important that it's version 1.0.1.
Once you have that, this code should work:
/*
================= REQUIRED USER ACCOUNT INFORMATION ==============================================
*/
var accountID = 'PUT ACCOUNT ID HERE';
var token = {
public: 'PUT TOKEN KEY HERE',
secret: 'PUB TOKEN SECRET HERE'
};
var consumer = {
public: 'PUT CONSUMER KEY HERE',
secret: 'PUT CONSUMER SECRET HERE'
};
//use the full restlet URL, not the rest.netsuite.com URL
//for example, https://YOURACCOUNTNUMBER.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=SCRIPTNUMBER&deploy=DEPLOYNUMBER
var restlet_url = 'PUT YOUR RESTLET URL HERE';
/*
=========================================================================================================
*/
//REQUIRED NPM MODULES
const request = require('request');
const OAuth = require('oauth-1.0a'); //version 1.0.1, don't do version 1.1.0
//SET UP THE OAUTH OBJECT
var oauth = OAuth({
consumer: consumer,
signature_method: 'HMAC-SHA256' //you can also use HMAC-SHA1 but HMAC-SHA256 is more secure (supposedly)
});
//SET UP THE REQUEST OBJECT
var request_data = {
url: restlet_url,
method: 'POST',
};
//GET THE AUTHORIZATION AND STICK IT IN THE HEADER, ALONG WITH THE REALM AND CONTENT-TYPE
var authorization = oauth.authorize(request_data, token);
var header = oauth.toHeader(authorization);
header.Authorization += ', realm="' + accountID + '"';
header['content-type'] = 'application/json';
//MAKE THE REQUEST
request({
url: request_data.url,
method: request_data.method,
headers: header,
json: {
message: "test123" //this is your payload
}
}, function(error, response, body) {
if(error)
{
console.log(error);
}
else
{
console.log(body);
}
});
If anybody has any problems with this code, leave a response and I'll do my best to help.
Netsuite's node samples use oauth-1.0a
https://netsuite.custhelp.com/app/answers/detail/a_id/42171
and their sample from https://netsuite.custhelp.com/app/answers/detail/a_id/42172/
is what I've had in production for a couple of years and works well.
var Promise = require('bluebird');
var request = require('request');
var crypto = require('crypto');
var OAuth = require('oauth-1.0a');
var Agent = require('https').Agent;
//var debug = require('debug')('kotn-ns');
function promiseTry(pSrc, maxTries, minDelay, maxDelay, canRetry){ //NS prone to spurious failures due to overloading
return new Promise(function(resolve, reject){
minDelay = minDelay || 0;
var delaySize = maxDelay - minDelay;
var t = function(){ return Math.floor(Math.random()* delaySize)+ minDelay;};
var firstReason = null;
function doRetry(triesLeft){
pSrc().then(function(data){
resolve(data);
}).catch(function(reason){
if(!firstReason) firstReason = reason;
console.error('in retry error with '+reason.toString());
if(triesLeft && canRetry(reason)) setTimeout(function(){ doRetry(triesLeft-1);}, t());
else reject(firstReason);
});
}
doRetry(maxTries -1);
});
}
function hasReason(msg, reasons){
for(var i = 0; i< reasons.length;i++){
if(msg.indexOf(reasons[i]) != -1) return true;
}
return false;
}
var agentPool = {};
function getAgent(accountId, tokenId){
var agentKey = accountId+'::'+ tokenId;
var agent = agentPool[agentKey];
if(!agent){
console.log('new agent for '+agentKey)
agent = new Agent({
keepAlive:false,
maxSockets:5
});
agentPool[agentKey] = agent;
}
return agent;
}
/**
* [RESTHandler description]
* #param {options} options {accountId, consumerKey,consumerSecret,tokenId,tokenSecret}
*/
function RESTHandler(options) {
var config = Object.assign({
maxTries:3,
minRetryDelay: 800,
maxRetryDelay:30000,
canRetry: function(reason){
var reasonText = reason.message || JSON.stringify(reason);
if(hasReason(reasonText, ['ECONNRESET', 'ESOCKETTIMEDOUT','ETIMEDOUT', 'SSS_REQUEST_LIMIT_EXCEEDED'])) {
console.error('retrying because: '+reasonText);
return true;
}
console.error('no retry with: '+reasonText);
return false;
}
}, options);
var oauth = OAuth({
consumer: {
key: config.consumerKey,
secret: config.consumerSecret
},
signature_method: 'HMAC-SHA1',
parameter_seperator: ',',
hash_function: function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
}
});
var token = {
key: config.tokenId,
secret: config.tokenSecret
};
function makeRequest(url, method, payload) {
var requestData = {
url: url,
method: method
};
if(payload){
requestData.body = payload;
}
var headers = oauth.toHeader(oauth.authorize(requestData, token));
headers.Authorization += ',realm="' + config.accountId + '"';
headers.authorization = headers.Authorization;
delete headers.Authorization;
headers['content-type'] = 'application/json';
headers['accept'] = 'application/json';
//console.log(JSON.stringify(headers, null, ' '));
requestData.headers = headers;
Object.assign(requestData, {
pool:getAgent(config.accountId, config.tokenId),
timeout : 30000,
strictSSL : true
});
// requestData.json = true;
// return new Promise(function(resolve){
// resolve({'headers' : 'done'});
// });
var processRequest = function(){
return new Promise(function(resolve, reject) {
request(requestData, function(error, response, body) {
if(error){
console.error('error calling: '+ requestData.url);
console.error(error);
reject((error instanceof Error) ? error : new Error(JSON.stringify(error)));
return;
}
if(!body || !(/"success"/).test(body)) {
console.log(method +' '+ response.statusCode +' '+ url +'\n\t'+body);
reject(new Error(body || 'unexpected error'));
return;
}
try{
resolve(JSON.parse(body));
}catch(e){
console.trace(e);
reject(e);
}
});
});
};
return promiseTry(processRequest, config.maxTries, config.maxRetryDelay, config.minRetryDelay, config.canRetry);
}
return{
get: function(url){
return makeRequest(url, 'GET');
},
put: function(url, data){
return makeRequest(url, 'PUT', data);
},
post: function(url, data){
return makeRequest(url, 'POST', data);
},
destroy : function(){
//nsAgent.destroy();
}
};
}
module.exports = RESTHandler;
More up-to-date as of 2022-12-12
replaced request with its successor needle and removed the now unnecessary bluebird
const needle = require('needle');
const crypto = require('crypto');
const OAuth = require('oauth-1.0a');
const Agent = require('https').Agent;
const debug = require('debug')('kotn-nso');
function promiseTry(pSrc, maxTries, minDelay, maxDelay, canRetry){ //NS prone to spurious failures due to overloading
return new Promise((resolve, reject)=>{
minDelay = minDelay || 0;
const delaySize = maxDelay - minDelay;
const t = function(){ return Math.floor(Math.random()* delaySize)+ minDelay;};
let firstReason = null;
function doRetry(triesLeft){
pSrc().then((data)=>{
resolve(data);
}).catch((reason)=>{
if(!firstReason) firstReason = reason;
console.error('in retry error with '+ triesLeft +' for '+reason.toString());
if(triesLeft && canRetry(reason)){
setTimeout(()=>{ doRetry(triesLeft-1);}, t());
}
else reject(firstReason);
});
}
doRetry(maxTries -1);
});
}
function hasReason(msg, reasons){
for(var i = 0; i< reasons.length;i++){
if(msg.indexOf(reasons[i]) != -1) return true;
}
return false;
}
var agentPool = {};
function getAgent(accountId, maxSockets){
var agentKey = accountId+'::'; // + tokenId;
var agent = agentPool[agentKey];
if(!agent){
console.log('new agent for '+agentKey);
agent = new Agent({
keepAlive:false,
maxSockets:maxSockets || 2 // one in reserve for slow closers
});
agentPool[agentKey] = agent;
}
return agent;
}
/**
* [RESTHandler description]
* #param {options} options {accountId, consumerKey,consumerSecret,tokenId,tokenSecret,maxSockets}
*/
function RESTHandler(options) {
const config = Object.assign({
maxTries:3,
minRetryDelay: 400,
maxRetryDelay:30000,
canRetry: function(reason){
var reasonText = reason.message || JSON.stringify(reason);
if(hasReason(reasonText, ['ECONNRESET', 'ESOCKETTIMEDOUT','ETIMEDOUT', 'SSS_REQUEST_LIMIT_EXCEEDED'])) {
console.error('retrying because: '+reasonText);
return true;
}
console.error('no retry with: '+reasonText);
return false;
}
}, options);
const oauth = OAuth({
consumer: {
key: config.consumerKey,
secret: config.consumerSecret
},
signature_method: 'HMAC-SHA256',
parameter_seperator: ',',
hash_function: function(base_string, key) {
return crypto.createHmac('sha256', key).update(base_string).digest('base64');
}
});
const token = {
key: config.tokenId,
secret: config.tokenSecret
};
function makeRequest(url, method, payload) {
debug(method +' '+ JSON.stringify(url));
var requestData = {
url: url,
method: method
};
if(payload){
requestData.body = payload;
}
var headers = oauth.toHeader(oauth.authorize(requestData, token));
headers.Authorization += ',realm="' + config.accountId + '"';
headers.authorization = headers.Authorization;
delete headers.Authorization;
headers['content-type'] = 'application/json';
headers['accept'] = 'application/json';
//console.log(JSON.stringify(headers, null, ' '));
const options = {
headers:headers,
agent:getAgent(config.accountId, config.maxSockets),
timeout : 30000,
strictSSL : true,
time:false
};
var processRequest = function(){
var headers = null;
return needle(method.toLowerCase(), url, payload, options).then(resp=>{
debug(resp.statusCode +' ' + JSON.stringify(resp.headers, null, ' '));
headers = resp.headers;
if(resp.statusCode != 200 && resp.statusCode != 201){
throw new Error(resp.statusCode +': '+ (resp.body || 'unexpected error'));
}
if(!resp.body) throw new Error('Unexpected Response');
return resp.body;
}).catch(error=>{
if(headers){
debug('Error returned with ' + JSON.stringify(headers));
}
const msg = error instanceof Error ? error.message : JSON.stringify(error);
console.error(method +' error on '+ url + ', '+ msg);
throw (error instanceof Error) ? error : new Error(msg);
});
};
return promiseTry(processRequest, config.maxTries, config.maxRetryDelay, config.minRetryDelay, config.canRetry);
}
return{
get: function(url){
return makeRequest(url, 'GET');
},
put: function(url, data){
return makeRequest(url, 'PUT', data);
},
post: function(url, data){
return makeRequest(url, 'POST', data);
},
destroy : function(){
//nsAgent.destroy();
}
};
}
module.exports = RESTHandler;

In NetSuite with SuiteScript 2.0 unable to send a file with HTTP POST request with a content-type multipart/form-data

I am not able to send my "multipart/form-data' file to this API. If I use POSTMAN it's working but with the https post method it seems that netsuite doesn't recognize the "form-data" content-type. Somebody knows how to send a form-data with SuiteScript 2 ? Here is a part of my code:
var fileObj = file.create({
name: invoiceNumber + '_ubl.xml',
fileType: file.Type.XMLDOC,
contents: einvoicecontentwithpdf,
folder : 120,
isOnline : false
});
var headers = {
'Authorization': 'Basic xxxxxxxxxxxxxxxxxxxxx',
'Content-Type': 'multipart/form-data'
};
var response = https.post({
url: 'https://community-api-uat-1-2.nxt.uat.unifiedpost.com/api/universal_connector/v1/uc/ar',
body: {
'file': fileObj
},
headers: headers
});
You have to build the whole body yourself.
This works as much as I've tested so far. Code may look a little non-idomatic because it's compiled from Typescript.
Note that to include non-text files this would have to be adapted to base64 encode the file bodies.
/**
* multiPartUpload.js
* #NApiVersion 2.x
*/
define(["require", "exports", "N/https", "N/file", "N/log"], function (require, exports, http, file, log) {
Object.defineProperty(exports, "__esModule", { value: true });
var types = {};
types[file.Type.AUTOCAD] = 'application/x-autocad';
types[file.Type.BMPIMAGE] = 'image/x-xbitmap';
types[file.Type.CSV] = 'text/csv';
types[file.Type.EXCEL] = 'application/vnd.ms-excel';
types[file.Type.FLASH] = 'application/x-shockwave-flash';
types[file.Type.GIFIMAGE] = 'image/gif';
types[file.Type.GZIP] = 'application/?x-?gzip-?compressed';
types[file.Type.HTMLDOC] = 'text/html';
types[file.Type.ICON] = 'image/ico';
types[file.Type.JAVASCRIPT] = 'text/javascript';
types[file.Type.JPGIMAGE] = 'image/jpeg';
types[file.Type.JSON] = 'application/json';
types[file.Type.MESSAGERFC] = 'message/rfc822';
types[file.Type.MP3] = 'audio/mpeg';
types[file.Type.MPEGMOVIE] = 'video/mpeg';
types[file.Type.MSPROJECT] = 'application/vnd.ms-project';
types[file.Type.PDF] = 'application/pdf';
types[file.Type.PJPGIMAGE] = 'image/pjpeg';
types[file.Type.PLAINTEXT] = 'text/plain';
types[file.Type.PNGIMAGE] = 'image/x-png';
types[file.Type.POSTSCRIPT] = 'application/postscript';
types[file.Type.POWERPOINT] = 'application/?vnd.?ms-?powerpoint';
types[file.Type.QUICKTIME] = 'video/quicktime';
types[file.Type.RTF] = 'application/rtf';
types[file.Type.SMS] = 'application/sms';
types[file.Type.STYLESHEET] = 'text/css';
types[file.Type.TIFFIMAGE] = 'image/tiff';
types[file.Type.VISIO] = 'application/vnd.visio';
types[file.Type.WORD] = 'application/msword';
types[file.Type.XMLDOC] = 'text/xml';
types[file.Type.ZIP] = 'application/zip';
function getContentType(f) {
var mime = types[f.fileType];
var charset = f.encoding;
var ct = 'Content-Type: ' + mime + (charset ? ';charset=' + charset : '');
log.debug({ title: 'content for ' + f.name, details: ct });
return ct;
}
function isFile(o) {
return (typeof o == 'object' && typeof o.fileType != 'undefined');
}
/**
* Creates a multipart upload
* #param {string} url to post to
* #param {object} headers key/value of headers; include Auth headers if needed
* #param {array} parts array of {name:string, value:file|string}
*/
function uploadParts(url, headers, parts) {
var boundary = 'someuniqueboundaryasciistring';
headers['content-type'] = 'multipart/form-data; boundary=' + boundary;
// Body
var body = [];
parts.forEach(function (p, idx) {
var partIsFile = isFile(p.value);
body.push('--' + boundary);
body.push('Content-Disposition: form-data; name="' + p.name + '"' + (partIsFile ? ('; filename="' + p.value.name + '"') : ''));
if (partIsFile) {
var ct = getContentType(p.value);
body.push(getContentType(p.value));
if(partIsBinary(ct)){
body.push('Content-Transfer-Encoding: base64');
}
}
body.push('');
body.push(partIsFile ? p.value.getContents() : p.value);
if (idx == parts.length - 1) {
body.push('--' + boundary + '--');
body.push('');
}
});
// Submit Request
try {
var response = http.post({
url: url,
headers: headers,
body: body.join('\r\n')
});
return response;
}
catch (e) {
log.error({ title: 'Failed to submit file', details: (e.message || e.toString()) + (e.getStackTrace ? (' \n \n' + e.getStackTrace().join(' \n')) : '') });
}
}
exports.uploadParts = uploadParts;
;
});
a simple test:
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(["require", "exports", "N/file", "./multiPartUpload"], function (require, exports, file, multiPartUpload_1) {
Object.defineProperty(exports, "__esModule", { value: true });
function onRequest(context) {
var files = [
{ name: 'f1', value: file.load({ id: 1056 }) }, // file cabinet ids; you can use dynamic files
{ name:'t1', value:'test'},
{ name: 'f2', value: file.load({ id: 7479 }) }
];
var resp = multiPartUpload_1.uploadParts('https://your-test-server/', {}, files);
context.response.write({ output: resp.body });
}
exports.onRequest = onRequest;
});

Multi part form-data image upload using cordova-file-transfer plugin in ios fails

I have to upload an image to server using multi-part image upload from my ionic project. Here is my code,
$scope.uploadImage = function(imageUrl) {
var fileName = imageUrl.substring(imageUrl.lastIndexOf('/')+1);
var json= {
"id":123,
"name" :fileName
}
var fileUploadOptions = new FileUploadOptions();
fileUploadOptions.fileKey="file";
fileUploadOptions.fileName = fileName;
fileUploadOptions.params = {
json : json
};
fileUploadOptions.mimeType="image/jpeg";
var URL = 'http://192.168.43.7:8080/services/uploadImage'
var encodedURI = encodeURI(URL);
console.log('fileUploadOptions : ',fileUploadOptions);
var ft = new FileTransfer();
ft.upload(imageUrl, encodedURI, onSuccess, onError, fileUploadOptions, false);
function onSuccess(response){
console.log('file uploaded: ',response);
}
function onError(error){
console.log('upload failed',error);
}
}
I am using the following plugins
cordova-plugin-file
cordova-plugin-file-transfer
cordova-plugin-camera
My image capture code is
$scope.takePhoto = function() {
var options = {
quality: 75,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: 1,
allowEdit: false,
encodingType: 0,
targetWidth: 1280,
targetHeight: 720,
popoverOptions: CameraPopoverOptions,
direction: 1,
saveToPhotoAlbum: true
};
var cameraSuccess = function(imageData) {
onPhotoURISuccess(imageData);
function onPhotoURISuccess(imageURI) {
createFileEntry(imageURI);
}
function createFileEntry(imageURI) {
window.resolveLocalFileSystemURL(imageURI, copyPhoto, fail);
}
function copyPhoto(fileEntry) {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSys) {
fileSys.root.getDirectory("photos", { create: true, exclusive: false }, function(dir) {
var fileName = 12 + "_" + 56 + "_" + 67 + ".jpg";
fileEntry.copyTo(dir, fileName, onCopySuccess, fail);
}, fail);
}, fail);
}
function onCopySuccess(entry) {
console.log('Full path: ', JSON.stringify(entry));
var path = entry.toURL();
$scope.imageUrl = path;
console.log('imageUrl: ',$scope.imageUrl);
}
function fail(error) {
console.error(JSON.stringify(error));
var cause = "";
if (error.code == 20) {
cause = "Camera permission denied"
}
}
}
var cameraError = function(error) {
console.log('camera error: ', error);
}
navigator.camera.getPicture(cameraSuccess, cameraError, options);
}
I am passing the $scope.imageUrl variable to upload function.
The code works fine in android devices.
But iOS, the upload fails.
I am getting
com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
error in my server console.
In my device console I am getting the following error,
upload failed
body: "An error has occurred. Please contact system administrator."
code: 3
exception: null
http_status: 500
source: "file:///var/mobile/Containers/Data/Application/8C4518AC-5606-4806-A8D2-216125EFE725/Documents/photos/12_56_57.jpg"
target: "http://192.168.43.7:8080/services/uploadImage"
The message in the body of the error is from my server.
As per the error I get from server, I came to know that, the JSON part is not getting uploaded to server. I tried to recreate the same issue with postman without sending the JSON object. I got the same error.
Do anyone know what is the issue ? Why only in iOS device this issue is there ?
From the docs:
params: A set of optional key/value pairs to pass in the HTTP request. (Object, key/value - DOMString)
Try using fileUploadOptions.params = { json : JSON.stringify(json) } instead.
var imageUrI="file:///storage/emulated/0/newfile.csv";
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = imageURI.substr(imageURI.lastIndexOf('/') + 1);
options.mimeType = "image/jpeg/csv";
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload(imageURI, "http://fileupload/admin/add_image_my.php",
function (result) {
console.log(JSON.stringify(result));
},
function (error) {
console.log(JSON.stringify(error));
}, options);

Send a recorded file via Filetransfer with Cordova/Phonegap

I am trying to send a voice recording that I recorded via the Media plugin.
When I try to send the file I get this FileError.NOT_FOUND_ERR error:
Error opening file /myRecording100.wav: Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0xa358640 {NSFilePath=/myRecording100.wav, NSUnderlyingError=0xa34fb30 "The operation couldn’t be completed. No such file or directory"}
2014-08-06 17:02:26.919 Bring Me[40961:c07] FileTransferError {
code = 1;
source = "/myRecording100.wav";
target = "http://XXXX.xom";
}
However, I can play the voice recording after recording it.
Why would I be able to play the file (showing that the file was recorded and saved correctly) but FileTransfer be unable to send it?
Here is my code (for ios):
var my_recorder = null;
var mediaFileFullName = null; // iOS
var mediaRecFile = "myRecording100.wav";
var checkFileOnly = false;
/******
Call when start recording
******/
function startRecording() {
checkFileOnly = false;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onSuccessFileSystem, function() {
console.log("***test: failed in creating media file in requestFileSystem");
});
}
function onSuccessFileSystem(fileSystem) {
if (checkFileOnly === true) {
// Get File and send
fileSystem.root.getFile(mediaRecFile, { create: false, exclusive: false }, onOK_GetFile, onFail_GetFile);
}
else {
// Create File
fileSystem.root.getFile(mediaRecFile, { create: true, exclusive: false }, onOK_SaveFile, onFail_GetFile);
}
}
/* Save the file*/
function onOK_SaveFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
my_recorder = new Media(mediaFileFullName,
function() { document.location ="address_form.html"; // Redirect the user to an other page },
function(err) { console.log("playAudio():callback Record Error: "+err);}
);
my_recorder.startRecord();
}
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
mediaFileFullName = fileEntry.fullPath;
/*
// Read the recorded file is WORKING !
my_player = new Media(mediaFileFullName, onMediaCallSuccess, onMediaCallError);
my_player.play();
*/
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(mediaFileFullName, "https://SERVER_ADDRESS", win, fail, options);
}
/******
Called when stop recording
******/
function stopRecording() {
if (my_recorder) {
my_recorder.stopRecord();
}
}
Since the v1.0 of File plugin, to upload a file in the filesystem via the file-transfer plugin, you'll need to use the .toURL() method to access to it.
If you are upgrading to a new (1.0.0 or newer) version of File, and
you have previously been using entry.fullPath as arguments to
download() or upload(), then you will need to change your code to use
filesystem URLs instead.
FileEntry.toURL() and DirectoryEntry.toURL() return a filesystem URL
of the form
So the correct code is :
/* Get the file and send it */
function onOK_GetFile(fileEntry) {
var options = new FileUploadOptions();
options.fileKey = "want";
options.fileName = "file.wav";
options.mimeType = "audio/wav";
options.chunkedMode = false;
options.params = parameters;
var ft = new FileTransfer();
ft.upload(fileEntry.toURL(), "https://SERVER_ADDRESS", win, fail, options);
}
I got the exact same issue on iOS,and FileUploadOptions didn't work for me.
In case someone is struggling as well, the solution for me has been to switch to LocalFileSystem.Temporary.
Here there is a snippet which shows a full example (not tested on Android):
var accessType = LocalFileSystem.TEMPORARY; // It was LocalFileSystem.PERSISTENT;
/** Utility function to return a fileEntry together with the metadata. */
var getFile = function(name, create, successCallback, failCallback) {
WL.Logger.debug("Request for file " + name + " received, create is " + create + ".");
var onSuccessFileSystem = function(fileSystem) {
fileSystem.root.getFile(name, { create: create, exclusive: false },
function(fileEntry){
WL.Logger.debug("Success, file entry for " + name + " is " + JSON.stringify(fileEntry));
fileEntry.getMetadata(function(metadata){
WL.Logger.debug("File entry " + name + " metadata is: " + JSON.stringify(metadata));
successCallback(fileEntry, metadata);
}, function(err) {
WL.Logger.debug("Fail to retrieve metadata, error: " + JSON.stringify(err));
if(failCallback) failCallback(err);
});
},
function(err) {
WL.Logger.error("Failed to retrieve the media file " + name + ".");
if(failCallback) failCallback(err);
});
}
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(accessType, 0, onSuccessFileSystem, function(err) {
WL.Logger.error("Failed to access file system.");
if(failCallback) failCallback(err);
});
};
var Recorder = declare([ ], {
mediaSrc : null,
mediaObj : null,
constructor : function(data, domNode){
this.mediaSrc = "new_recording.wav";
},
startRecord : function() {
var self = this;
var startRecording = function(source) {
var onMediaCallSuccess = function() { WL.Logger.debug("Media object success."); };
var onMediaCallError = function(err) { WL.Logger.error("Error on the media object: " + JSON.stringify(err)); };
self.mediaObj = new Media(source, onMediaCallSuccess, onMediaCallError);
self.mediaObj.startRecord();
};
// On iOS, first I need to create the file and then I can record.
if (deviceCheck.phone.ios) {
WL.Logger.debug("iOS detected, making sure the file exists.");
getFile(this.mediaSrc, true, function(fileEntry){ startRecording(fileEntry.fullPath); });
} else {
if (!deviceCheck.phone.android)
WL.Logger.warn("Don't know the device, trying to record ...");
else
WL.Logger.debug("Android detected.");
startRecording(this.mediaSrc);
}
},
stopRecord : function() {
this.mediaObj.stopRecord();
this.mediaObj.release();
},
play: function() {
var p,
playSuccess = function() { WL.Logger.debug("Play success."); p.release(); },
playFail = function() { WL.Logger.debug("Play fail."); };
p = new Media(this.mediaSrc, playSuccess, playFail);
p.play();
},
getData : function(successCallback, failCallback) {
var fileName = (deviceCheck.phone.android ? "/sdcard/" : "") + this.mediaSrc;
WL.Logger.debug("Asking for the file entry ... ");
getFile(this.mediaSrc, false,
function(fileEntry, metadata) {
WL.Logger.debug("Success: I found a file entry: " + fileEntry.nativeURL + ", size is " + metadata.size);
fileEntry.file(function(file) {
WL.Logger.debug("Success: file retrieved!");
var reader = new FileReader();
reader.onloadend = function(evt) {
WL.Logger.debug("Sending content and event data to success callback.");
successCallback(this.result, metadata, evt);
};
reader.readAsDataURL(file);
}, function(err){
WL.Logger.error("Error: Impossible to retrieve the file");
failCallback(err);
})
}, function(err){
WL.Logger.error("Fail: no file entry found: " + JSON.stringify(err));
failCallback(err);
});
}
});
There is a bit of Worklight (debug output) and dojo (declare), but this code could be useful as reference.

How to upload powerpoint file to server phonegap?

Hello I Want to upload a powerpoint file using phonegap file transfer protocol to my local java server thru ios simulator, the location of the file on the phone is passed to the handleOpenURL function when the user selects to open a powerpoint with my app. The problem is that nothing is happening although im sure this method is executing??!! can anyone help please?
function handleOpenURL(url)
{
setTimeout(function() {
alert(url);
jQuery.get( "http://192.168.1.100:8080/PpServer/getnumberofslides" , function( data ) {
numberofslides=data;
alert( "Load was performed." + data );
});
fileURL = url;
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
// processapplication();
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://192.168.1.100:8080/NewServerlet");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="multipart/form-data";
options.httpMethod="Post"
// options.params = {"file"};
var headers={'headerParam':'file'};
// options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
}, 0);
//
}

Resources