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)
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);
}
}
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.
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(" ")) + $scope.blocktitle +
'</h2><p>' + (new Array(lvl + 5).join(" ")) + ' path : '+ path + '</p>';
if($scope.blockvalue)
{
div.innerHTML = div.innerHTML + '<p>' + (new Array(lvl + 5).join(" ")) +
$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');
});