Can't upload image to remote server using Appcelerator - request

The application I'm working uses an API developed by another team. I'm working on Titanium 2.1.2 and I'm trying to upload a photo using said API. I'm using Appcelerator's HTTPClient to make the request. Here's my code:
var url = 'http://api.veramiko.com/albums/' + album.veramiko_id + '/photos';
var photo = imageView.toBlob();
var args = { //parameters sent to post photo
file : photo,
description : descriptionText
};
var client = Ti.Network.createHTTPClient({
onload : function(e){
Ti.API.info(this.responseText); //Print the result
},
onerror : function(e){
Ti.API.error(this.responseText); //Print the result
},
timeout : 60000
});
client.open('POST', url);
client.setRequestHeader('Authorization', 'Bearer ' + token);
client.setRequestHeader('Content-type', "multipart/form-data");
client.send(args);
Token is a variable we use to authorize any requests made to the server. I thought that by only converting the image from the ImageView into Blob would be enough to send the photo, but the photo isn't uploaded. The post is created with the description but the photo isn't sent properly.
Do I need to add something else? Is it right to send the photo as a Blob?
EDIT: I read this link and I tried the following with no result:
var url = 'http://api.veramiko.com/albums/' + album.veramiko_id + '/photos';
var boundary = '-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
var photo = imageView.toBlob();
var args = {
file : photo,
description : descriptionText.value
};
var contentDisposition = "--" + boundary + "\r\n";
contentDisposition += "Content-Disposition: form-data; name=\"file\";";
contentDisposition += "filename=\"" + imageView.image + "\"\r\n\";";
contentDisposition += "Content-Type: application/octet-stream\r\n\r\n";
var fullContent = contentDisposition + photo + "\r\n--" + boundary + "--";
alert(JSON.stringify(args));
var token = JSON.parse(Ti.App.Properties.getString('loggedUser', 'No existe')).networks[0].token;
var client = Ti.Network.createHTTPClient({
onload : function(e){
Ti.API.info(this.responseText); //Print the result
},
onerror : function(e){
Ti.API.error(this.responseText); //Print the result
},
timeout : 60000
});
client.open('POST', url);
client.setRequestHeader('Authorization', 'Bearer ' + token);
client.setRequestHeader('Content-Type', "multipart/form-data; boundary=\"" + boundary + "\"");
client.setRequestHeader('Connection', 'close');
client.send(fullContent);
I tried to wrap the file with a Content-Disposition and Content-Type header with no result.

I finally found a way to solve this. Please refer to the following link.
This is how my code looked in the end:
// I put the contents of my image into a variable
var f = imageHolder.toImage();
// I create a random name for the image
var tmpName = (new Date()).getTime().toString() + '.png';
// I create a folder where I will store the photo temporally
var g = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'picturesToUpload');
if (!g.exists()) {
// If the directory doesn't exist, make it
g.createDirectory();
};
var photo = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'picturesToUpload', tmpName);
// write the contents of the image into the file
photo.write(f);
// I create my url with the albumId where I'll upload the picture
var url = 'http://api.veramiko.com/albums/' + albumId + '/photos';
var args = {
file : photo.read(), // I read the contents of the file
description : ''
}
var token = JSON.parse(Ti.App.Properties.getString('loggedUser', 'No existe')).networks[0].token;
var client = Ti.Network.createHTTPClient({
onload : function(e){
Ti.API.info('Info received from the uploading: ' + this.responseText);
},
onerror : function(e){
Ti.API.debug('Error: ' + this.responseText);
},
timeout : 60000
});
client.open('POST', url);
// these headers are important
client.setRequestHeader('enctype', 'multipart/form-data');
client.setRequestHeader('Content-Type', 'image/png');
client.setRequestHeader('Authorization', 'Bearer ' + token);
client.send(args);
Hope this info helps more people.

First of all you Clear what is you API Parameter. and where you want to use TOKEN.
var imageView = Ti.UI.createImageView({
backgroundImage : "image.png",
});
var url = 'http://api.veramiko.com/albums/' + album.veramiko_id + '/photos';
var args = { //parameters sent to post photo
file : imageView.backgroundImage,
description : descriptionText,
token : "Token",
};
var client = Ti.Network.createHTTPClient({
onload : function(e){
Ti.API.info(this.responseText); //Print the result
},
onerror : function(e){
Ti.API.error(this.responseText); //Print the result
},
timeout : 60000
});
client.open('POST', url);
client.setRequestHeader("Content-type","multipart/form-data");
client.setRequestHeader("Content-length", args.length);
client.send(args);
Try This, I thought this is working for you...

I found this tutorial, that covers on how to build an uploader.
Titanium Mobile: Build an Image Uploader

You case use this code to upload a file in form-data :
var baseurlAPI = "YOUR API BASEURL HERE";
var file = Ti.Filesystem.getFile(pathToFile);
if(file.exists()){
var xhr = Ti.Network.createHTTPClient({
onload: function(e) {
Ti.API.log('success '+this.responseText);
},
onerror: function(e) {
Ti.API.error(this.responseText);
},
timeout : -1
});
xhr.open('POST', baseurlAPI);
xhr.send({
file: file
});
}else{
console.log('didnt exist ' + file.nativePath);
}

Related

Cordova Camera Image Not Uploading Using File Transfer

The idea of what I'm currently doing is that you can take a photo or select one from the gallery and upload it to a server. I was having trouble with using the cordova FileTransfer to send and upload the image. It was either not sending at all or $_FILES["file"] would be empty.
I have separate buttons to bring up the camera and gallery:
<button id="takePicture" name="takePicture" ng-click="openCamera();">Take Photo</button>
<button id="getPicture" name="getPicture" ng-click="openGallery();">Choose From Gallery</button>
Solution:
$scope.openCamera = function()
{
navigator.camera.getPicture(onSuccess, onError,
{ quality : 100,
destinationType : Camera.DestinationType.FILE_URI
});
function onSuccess(imageURI)
{
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = imageURI.substr(imageURI.lastIndexOf("/")+1);
options.mimeType = "image/jpeg";
options.httpMethod = "POST";
options.chunkedMode = false;
options.params = { filePath : imageURI.split("?")[0] };
var fileTransfer = new FileTransfer;
fileTransfer.upload(imageURI, encodeURI("upload.php"), uploadComplete, uploadError, options);
function uploadComplete(result)
{
console.log("Code = " + result.responseCode);
console.log("Response = " + result.response);
console.log("Sent = " + result.bytesSent);
}
function uploadError(error)
{
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
}
function onError(message)
{
alert("fail");
alert('Failed because: ' + message);
}
}
The data sent will then be received on the upload.php file. You should be able to check if the data has been sent by inspecting the files var_dump($_FILES);
As Sletheren mentioned it can also be done using $cordovaFileTransfer.upload();
This is how it works on my side:
First thing: The destinationType should be Camera.DestinationType.FILE_URI,
Second: Set the filename to: filename = imageURI.split('?')[0];
Third: the fileTransfer takes these arguments (it works on my side) :
$cordovaFileTransfer.upload(url, filename, options)

http.get attempts to retrieve image when parsing when I do not need it to

function getHTMLSource(url) {
return $http.get(url).then(function(response) {
var html = response.data;
url = getDetailPage(html)
return url ;
}
)};
getHTMLSource('http://www.footpatrol.co.uk/s:282524/search=282524/')
Whenever I attempt the above code, I get the following error message in my console GET http://localhost:9000/templates/footpatrol.co.uk/_assets/images/content/footpatrol_logo.png 404 (Not Found).
The image appears to exist at http://www.footpatrol.co.uk/templates/footpatrol.co.uk/_assets/images/content/footpatrol_logo.png but as I am running the scipt on localhost with Chrome's Allow-Control-Allow-Origin plugin, it appears to not play nicely. I do not want to GET the image I just want the source code, is there anyway around this?
UPDATE: I think it might be my parser causing the problem, because the error message is thrown here
function getDetailPage(html) {
var temp = document.createElement('div');
temp.innerHTML = html;
var a = temp.querySelector('a[class*=\'fp-product-thumb-link\']');
var partOfUrl = a.href;
var splitUrl = partOfUrl.split('/');
var url = 'http://www.footpatrol.co.uk/' + splitUrl[3] + '/' + splitUrl[4];
var url = 'http://www.footpatrol.co.uk/' + splitUrl[3] + '/' + splitUrl[4];
$log.debug('Detail page url found: ' + url);
return url;
}
Instead of creating an element, use the DOMParser API:
function getDetailPage(html) {
//var temp = document.createElement('div');
//temp.innerHTML = html;
var parser = new DOMParser();
var temp = parser.parseFromString(html, "text/html");
var a = temp.querySelector('a[class*=\'fp-product-thumb-link\']');
//var partOfUrl = a.href;
//console.log(partOfUrl);
//var splitUrl = partOfUrl.split('/');
//var url = 'http://www.footpatrol.co.uk/' + splitUrl[3] + '/' + splitUrl[4];
var elem = angular.element(a);
var url = 'http://www.footpatrol.co.uk/' + elem.attr('href');
console.debug('Detail page url found: ' + url);
return url;
}
Detail page url found: http://www.footpatrol.co.uk//footwear/282524-air-retro-15-obsidian.html

Zipping multiple files in Nodejs having size ~ 300kb each and streaming to client

My code is working fine when I zip 3 files around 300kb each and send it to client. Used following links for help:
Dynamically create and stream zip to client
how to convert multiple files to compressed zip file using node js
But as soon as I try to zip 4th file I get "download - Failed Network error" in chrome.
Following is my code:
var express = require('express');
var app = express();
var fileSystem = require('fs');
var Archiver = require('archiver');
var util = require('util');
var AdmZip = require('adm-zip');
var config = require('./config');
var log_file = fileSystem.createWriteStream(__dirname + '/debug.log', {flags : 'a'});
logError = function(d) { //
log_file.write('[' + new Date().toUTCString() + '] ' + util.format(d) + '\n');
};
app.get('/zip', function(req, res, next) {
try {
res = setHeaderOfRes(res);
sendZip(req, res);
}catch (err) {
logError(err.message);
next(err); // This will call the error middleware for 500 error
}
});
var setHeaderOfRes = function (res){
res.setHeader("Access-Control-Allow-Origin", "*"); //Remove this when this is on production
res.setHeader("Content-Type", "application/zip");
res.setHeader("Content-disposition", "attachment;");
return res;
};
var sendZip = function (req, res) {
var filesNotFound = [];
zip.pipe(res);
if (req.query.leapIds) {
var leapIdsArray = req.query.leapIds.split(',');
var i, lengthi;
for (i = 0, lengthi = leapIdsArray.length; i < lengthi; i++) {
try {
var t = config.web.sharedFilePath + leapIdsArray[i] + '.vsdx';
if (fileSystem.statSync(t).isFile()) {
zip.append(new fileSystem.createReadStream(t), {
name: leapIdsArray[i] + '.vsdx'
});
};
} catch (err) {
filesNotFound.push(leapIdsArray[i] + '.vsdx');
}
}
var k, lengthk;
var str = '';
for (k = 0, lengthk = filesNotFound.length; k < lengthk; k++) {
str += filesNotFound[k] +',';
}
if(filesNotFound.length > 0){
zip.append('These file/files does not exist on server - ' + str , { name: 'logFile.log' });
}
zip.finalize();
}
};
I tried zip.file instead of zip.append that didn't work.
I want to zip minimum 10 files of 300kb each and send it to the client. Can anyone please let me know the approach.
Thanks
/********************* Update ****************************************
I was only looking at server.js created in node. Actually the data is sent correctly to client. Angularjs client code seems to be not working for large files.
$http.get(env.nodeJsServerUrl + "zip?leapIds=" + nodeDetails, { responseType: "arraybuffer" }
).then(function (response) {
nodesDetails = response.data;
var base64String = _arrayBufferToBase64(nodesDetails);
function _arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
var anchor = angular.element('<a/>');
anchor.attr({
href: 'data:application/zip;base64,' + base64String,
target: '_blank',
download: $scope.main.routeParams.sectorId + "-ProcessFiles.zip"
})[0].click();
});
This part href: 'data:application/zip;base64,' + base64String, seems to be failing for large data received from server. For small files it is working. Large files it is failing.
Found out.
The problem was not in nodejs zipping logic. That worked perfect.
Issue was in the way I was handling the received response data.
If the data that is received is too large then following code fails
anchor.attr({
href: 'data:application/zip;base64,' + base64String,
target: '_blank',
download: $scope.main.routeParams.sectorId + "-ProcessFiles.zip"
})[0].click();
so the work around is to use blob:
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, { type: contentType });
return blob;
}
var contentType = 'application/zip'
var blob = b64toBlob(base64String, contentType);
saveAs(blob, "hello world.zip");
This link helped me out: How to save binary data of zip file in Javascript?
already answered here: https://stackoverflow.com/a/62639710/8612027
Sending a zip file as binary data with expressjs and node-zip:
app.get("/multipleinzip", (req, res) => {
var zip = new require('node-zip')();
var csv1 = "a,b,c,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
zip.file('test1.file', csv1);
var csv2 = "z,w,x,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
zip.file('test2.file', csv2);
var csv3 = "q,w,e,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
zip.file('test3.file', csv3);
var csv4 = "t,y,u,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
zip.file('test4.file', csv4);
var data = zip.generate({base64:false,compression:'DEFLATE'});
console.log(data); // ugly data
res.type("zip")
res.send(new Buffer(data, 'binary'));
})
Creating a download link for the zip file. Fetch data and convert the response to an arraybuffer with ->
//get the response from fetch as arrayBuffer...
var data = response.arrayBuffer();
const blob = new Blob([data]);
const fileName = `${filename}.${extension}`;
if (navigator.msSaveBlob) {
// IE 10+
navigator.msSaveBlob(blob, fileName);
} else {
const link = document.createElement('a');
// Browsers that support HTML5 download attribute
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}

upload dynamically generated pdf from aws-lambda into aws-s3

In my serverless app, I want to create pdf which is generated dynamically and then upload that created pdf into aws s3. My problem is, when a url is returned to client-side code from server, uploaded url doesn't working. My code is given below:
Client-side javascript code (angular.js)
$scope.downloadAsPDF = function() {
// first I have to sent all html data into server
var html = angular.element('html').html(); // get all page data
var service = API.getService();
service.downloadPdf({}, { html : html }, // api call with html data
function(res) {
console.log("res : ", res);
window.open(res.url); // open uploaded pdf file
// err: The server replies that you don't have permissions to download this file
// HTTP/1.1 403 Forbidden
}, function(err) {
console.log("err : ", err);
});
};
Serverless Code
var fs = require('fs');
var pdf = require('html-pdf');
var AWS = require("aws-sdk");
var s3 = new AWS.S3();
module.exports.handler = function(event, context) {
if (event.html) { // client html data
AWS.config.update({
accessKeyId: 'xxxxxxxxxxxxxxxxx',
secretAccessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
region: 'xxxxxxxxxxxxxxxxxxxx'
});
var awsInfo = {
bucket: 'xxxxx-xxxxxx'
};
var baseUrl = 'https://s3-my-region.amazonaws.com/s3-upload-directory';
var folderRoot = 'development/pdf';
// unique file name
var output_filename = Math.random().toString(36).slice(2) + '.pdf';
// file created directory
var output = '/tmp/' + output_filename;
pdf.create(event.html, options).toStream(function(err, stream) {
if( err ) {
console.log('pdf err : ', err);
} else {
writeStream =fs.createWriteStream(output);
s3.putObject({
Bucket : awsInfo.bucket,
Key : folderRoot + '/' + output_filename,
Body : fs.createReadStream(output),
ContentType : "application/pdf"
},
function(error, data) {
if (error != null) {
console.log("error: " + error);
} else {
// upload data: { ETag: '"d41d8cd98f00b204e9800998ecf8427e"' }
console.log('upload data : ', data);
return cb(null, {
// return actual aws link, but no file
// ex: 'https://s3-my-region.amazonaws.com/s3-upload-directory/output_filename.pdf
url: baseUrl + '/' + output_filename
});
}
});
}
}
};
I've solve my problem. I was trying to upload pdf before I generate pdf. I have solve this problem using the following code:
pdf.create(event.html, options).toStream(function(err, stream) {
if (err) {
console.log('pdf err : ', err);
} else {
var stream = stream.pipe(fs.createWriteStream(output));
stream.on('finish', function () {
s3.putObject({
Bucket : awsInfo.bucket,
Key : folderRoot + '/' + output_filename,
Body : fs.createReadStream(output),
ContentType : "application/pdf"
},
function(error, data) {
if (error != null) {
console.log("error: " + error);
return cb(null, {
err: error
});
} else {
var url = baseUrl + '/' + output_filename
return cb(null, {
url: url
});
}
});
});
}
});
I have done similar kind of thing before. I want a few clarifications from you and then I will be able to help you better.
1) In your code (server side), you have mentioned in the callback function that actual aws link is getting returned.
Are you sure that your file is getting uploaded to Amazon s3. I mean did you check your bucket for the file or not?
2) Have you set any custom bucket policy on Amazon s3. Bucket policy play an important role in what can be downloaded from S3.
3) Did you check the logs to see exactly which part of code is causing the error?
Please provide me this information and I think the I should be able to help you.
if we don't want to upload at s3 just return generated file from aws-lambda.

serialize a client-side made string to a server-side file angularJS

So i'm building a string which is actually an xml file. Since i can't save any file from client-side for security issues, i'd like to save this string in a xml file server-side.
i've read about $http method or upload plugin but it seems a bit a complicated for just sending a string to my server.
is there an easier way to do it?
For informative purpose, here is my code :
$scope.addBlock = function(){
if($scope.parentblock == null)//creation of a lvl1 element
{
var div = document.createElement("div");
//xml creation
var elmt = document.createElement($scope.selectedtype.tagname);
//
div.setAttribute('id',$scope.blocktitle);
div.setAttribute('lvl',0);
var path = '/'+$scope.selectedtype.tagname;
div.setAttribute('path',path);
div.innerHTML = '<h1>' + $scope.blocktitle + '</h1><p> path : '+ path + '</p>';
if($scope.blockvalue)
{
div.innerHTML = div.innerHTML + '<p>' + $scope.blockvalue + '</p>';
//adding value to xml tag
elmt.innerHTML = $scope.blockvalue;
//
}
document.getElementById('blocks').appendChild(div);
//xml adding
doc.appendChild(elmt);
console.log(doc);
//
$scope.blocks.push([$scope.blocktitle,$scope.selectedtype.tagname,$scope.parentblock,path]);
}
else //creation of a lvl n element
{
var div = document.createElement("div");
//xml creation
var elmt = document.createElement($scope.selectedtype.tagname);
//
div.setAttribute('id',$scope.blocktitle);
var lvl = Number(document.getElementById($scope.parentblock[0]).getAttribute('lvl'))+1;
div.setAttribute('lvl',lvl);
var path = ($scope.parentblock[3]+'/'+$scope.selectedtype.tagname);
div.innerHTML = '<h2>' + (new Array(lvl + 1).join("&nbsp")) + $scope.blocktitle +
'</h2><p>' + (new Array(lvl + 5).join("&nbsp")) + ' path : '+ path + '</p>';
if($scope.blockvalue)
{
div.innerHTML = div.innerHTML + '<p>' + (new Array(lvl + 5).join("&nbsp")) +
$scope.blockvalue + '</p>';
//adding value to xml tag
elmt.innerHTML = $scope.blockvalue;
//
}
document.getElementById($scope.parentblock[0]).appendChild(div);
//xml adding
doc.getElementsByTagName($scope.parentblock[1].toLowerCase())[0].appendChild(elmt);
//
$scope.blocks.push([$scope.blocktitle,$scope.selectedtype.tagname,$scope.parentblock,path]);
}
//console.log(doc);
}
I need to send doc to my server.
EDIT :
i really need to send an xml since is a iso19110-normalized file that i have to save.
I tried this :
$http({
method: 'POST',
url: 'save.php',
data: { 'doc' : doc.outerHTML },
headers: {'Content-Type': 'text/xml'}
})
.success(function(data){
alert(data);
})
.error(function(){
alert('fail');
});
and this in php :
file_put_contents('test.xml', $_POST['doc'])
but i've got "index doc undefined" ...
I also tried
$http.post('save.php',doc).success(function() {
return console.log('uploaded');
});
but i got 'Converting circular structure to JSON'
So i think my problem come from the location of the data. I can't figure where it is...
If you need to send a lump of text to your server (over http) then you should use the $http.post() method. Its easy to use and works like a charm. You can receive and save the data on the server in any number of ways, presumably you have this bit worked out?
The real issue from your question, imo, is whether you really need to be sending xml? A much easier way of sending your data would be to use JSON. Then you don't need to go through all that document building rigmarole. Instead you can just build a javascript object and send it using the $http.post() method letting angular do all the work for you.
var doc;
doc = {
id: $scope.blocktitle,
lvl: 0
// rest of your doc structure here
};
$http.post('/my-url/upload/doc', doc).success(function() {
return console.log('uploaded');
});

Resources