Saving/downloading binary (buffer?) as a pdf - angularjs

User uploads the pdf file which gets saved to mongo as a binary, files are small in size (<1mb) so I am not using GridFS.
How to download the pdf back?
Here is what I have tried:
I am extracting the binary from mongo and sending it back to the user. I am using Hapi, sending it back goes simply as
res(binaryMongoPDF).code(200);
I am receiving same data I have store in mongo on the client. According to what I have read I should convert the binary to blob:
var blob = new Blob([data], { type: 'application/pdf' });
I have tried with and without setting responseType to arraybuffer:
$http.get(API_URL + "/cv/me", {responseType:'arraybuffer'})
and then just let user download it, I am simplifying that with Angular FileSave, but I have also tried with vanilla a tag creation approach:
FileSaver.saveAs(r, 'text.pdf');
The file I am getting back can't be opened as a pdf.
Something I have observed is that binary file I am saving in mongo has size 73262, but the blob has size 97426. I have no idea if this is normal or not.

If the server returns the file as a data url:
data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUgA
Use this directive:
angular.module("myApp").directive("xdHref", function() {
return function linkFn (scope, elem, attrs) {
scope.$watch(attrs.xdHref, function(newVal) {
newVal && elem.attr("href", newVal);
});
};
});
To set the href attribute of an <a> tag with the data:
<a download="data_{{files[0].name}}" xd-href="data">
<button>Download data</button>
</a>
The DEMO on PLNKR

Related

How to download a text file based on a generated String using AngularJS?

I'm trying to get AngularJS to trigger a file download when a user clicks on a button.
The file that should download has to contain data that is generated during the script execution, so the text file's content should be built from a basic string in AngularJS.
How do I implement this?
Here is some mockup code on how I imagine this would work:
var fileContent = 'foobar';
$scope.download = function() {
filedownload.run(fileContent, 'filename.txt');
}
In order to achieve this you have to create an a tag in your HTML:
<a download="content.txt" ng-href="{{ url }}">download</a>
Controller:
var content = 'file content for example';
var blob = new Blob([ content ], { type : 'text/plain' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );
And to enable the URL:
app = angular.module(...);
app.config(['$compileProvider',
function ($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/);
}]);
Source:
How do you serve a file for download with AngularJS or Javascript?
In simple cases, you can create a link with a Data URI:
var fileContent = 'foobar';
$scope.download = 'data:text/plain;base64,' + btoa(fileContent);
And then use it in your HTML template:
click here to download
You may need to adapt the MIME type, depending on your string.

Upload file to server using ionic

I am building a mobile app using ionic. One of the usecase is to let the user browse a file and upload it to the backend server (which exposes a rest service).
On the UI, I am using the html file tag
<input type="file" ng-select="uploadFile($files)" multiple>
This opens a file browser to select a file. Then in the controller, I am doing the following
.controller('UploadDocCtrl', function ($scope, $cordovaFileTransfer) {
$scope.uploadFile = function(files) {
console.log("selected file "+files);
// hard coded file path "/android_asset/www/img/ionic.pdf" to be replaced with the user selected file
$cordovaFileTransfer.upload(restServiceEndpoint, "/android_asset/www/img/ionic.pdf", properties).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
});
The problem is that I am not able to get a reference to the selected file. Can someone please help with the steps to achieve this. Thanks!

Parsing a CSV file provided by an input in Angular

I'm creating a webapp which features uploads of .csv file.
Instead of uploading the whole file on my server, then adding every lines in a table thanks to PHP, I'd like to parse the file in Angular, then add parsed lines (thanks to Restangular). This way, I should be able to retrieve some "errors" before uploading them (or not).
In a view, I've created a simple file input, with a directive called file (source : http://angularjstutorial.blogspot.fr/2012/12/angularjs-with-input-file-directive.html)
<input type="file" data-file="param.file" />
The filedirective :
app.directive('file', function(){
return {
scope: {
file: '='
},
link: function(scope, el, attrs){
el.bind('change', function(event){
var files = event.target.files;
var file = files[0];
scope.file = file ? file.name : undefined;
scope.$apply();
});
}
};
});
This way, I can retrieve when the user chooses a file. Unfortunately, I'm getting only the file name.
I'd like to parse the csv in a string, which i'll split thanks to this filter (source : parsing CSV in Javascript and AngularJS) :
app.filter('csvToObj',function(){
return function(input){
var rows=input.split('\n');
var obj=[];
angular.forEach(rows,function(val){
var o=val.split(';');
obj.push({
designation:o[1],
...
km:o[11]
});
});
return obj;
};
});
How can I retrieve the data in the csv file provided by the input instead of the file name ?
Thanks in advance.
Ok, I found the solution by searching on existing modules to upload files. I just post it here if this interests someone.
In the view, I've changed the directive to trigger the event :
<input type="file" file-change="handler($event,files)" ng-model="MyFiles" />
The directive is now :
app.directive('fileChange',['$parse', function($parse){
return{
require:'ngModel',
restrict:'A',
link:function($scope,element,attrs,ngModel){
var attrHandler=$parse(attrs['fileChange']);
var handler=function(e){
$scope.$apply(function(){
attrHandler($scope,{$event:e,files:e.target.files});
});
};
element[0].addEventListener('change',handler,false);
}
}
}]);
In the controller (don't forget to add $filter in the controller if you want to use it) :
$scope.MyFiles=[];
$scope.handler=function(e,files){
var reader=new FileReader();
reader.onload=function(e){
var string=reader.result;
var obj=$filter('csvToObj')(string);
//do what you want with obj !
}
reader.readAsText(files[0]);
}
The filter is still the same (except I've shifted the rows array to not import the header of my csv files).

get source and name of selected file with angularjs

Is there a way to get name, path and size of selected file in input field using angularJS,
before uploading it?
<input type="file" ng-model="fileContent" on-read-file="showContent($fileContent)" />
$scope.showContent = function($fileContent){
$scope.content = $fileContent;
};
Can anyone help to solve this please?
The HTML5 File API will give you a File object for each file that you're attempting to upload. This File object will have a size and name property which will give you the file size in bytes and the name of the file.
There's no property for the physical path to the file on the users machine, though.
You can read more about this on MDN: https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
More information on the File object here: https://developer.mozilla.org/en-US/docs/Web/API/File
Here's a working example: http://jsfiddle.net/fmeLz9cd/
Given an input of type file with an id fileSelected, here's an example of accessing the properties through the File API:
$('#fileSelected').on('change', function (evt) {
var files = $(evt.currentTarget).get(0).files;
if(files.length > 0) {
$('#fileName').text(files[0].name);
$('#fileSize').text(files[0].size);
$('#filePath').text($('#fileSelected').val());
}
});
Update
Since you've requested an AngularJS specific example, here's the same code working in an angular app:
http://jsfiddle.net/vyc6jq84/1/
<div ng-app="fileDemo">
<input type="file" fd-input />
</div>
var app = angular.module('fileDemo', []);
app.directive('fdInput', [function () {
return {
link: function (scope, element, attrs) {
element.on('change', function (evt) {
var files = evt.target.files;
console.log(files[0].name);
console.log(files[0].size);
});
}
}
}]);
You cannot, unless the server helps. Angular is running in your browser, and cannot read the filesystem of the server or of the browser's computer. If you want to get a file, you need the server to implement something that will do so.
Now, if it is a static file, and already is served up by the server, then you could read it via $http.
$http.get(filename).success(function(data){
// data contains the file content
});
But it may be interpreted based on the file type, etc. And this entirely presumes the file is already being served by the server.

Force download of 'data:text/plain' URL

I was wondering whether it is possible to force a browser (at least Chrome) to download a data:text/plain URL.
Chrome does download binary URLs (e.g. data:application/zip;base64,...), but it does not download files that can be viewed inside the browser (such as text files).
What I already tried with no luck so far is this:
data:text/plain;content-disposition=attachment;filename=test.txt;...
But it seems like I cannot add headers like this.
Is there any way to make Chrome download a data:text/plain,... URL?
As of now, it has been made possible to use <a download> in Chrome. Using dispatchEvent, you can download any string as file (even with a custom filename) whenever you want. Here's a utility function to use it:
var downloadFile = function(filename, content) {
var blob = new Blob([content]);
var evt = document.createEvent("HTMLEvents");
evt.initEvent("click");
$("<a>", {
download: filename,
href: webkitURL.createObjectURL(blob)
}).get(0).dispatchEvent(evt);
};
Usage:
downloadFile("foo.txt", "bar");
It uses jQuery and the webkit prefix, but both can be avoided.
Try this:
<a download="file_downloaded_via_data_URL.txt"
href="data:text/plain;base64,SGVsbG8sIHdvcmxkISBJJ20gZG93bmxvYWRlZCB2aWEgImRhdGE6dGV4dC9wbGFpbjsuLi4iIFVSTCB1c2luZyA8YSBkb3dubG9hZD0iZmlsZV9uYW1lIi4uLj4uDQpNeSBiaXJ0aHBsYWNlOiBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzY0Njg1MTcvDQoNCk1vcmUgYWJvdXQ6DQpodHRwOi8vd3d3LnczLm9yZy9UUi9odG1sL2xpbmtzLmh0bWwjYXR0ci1oeXBlcmxpbmstZG93bmxvYWQNCmh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWwvbGlua3MuaHRtbCNkb3dubG9hZGluZy1yZXNvdXJjZXMNCg0KQnJvd3NlciBzdXBwb3J0OiBodHRwOi8vY2FuaXVzZS5jb20vZG93bmxvYWQ=">
Download text file
</a>
It uses HTML5 attribute download="filename.ext". (no JS needed:)
More about:
http://www.w3.org/TR/html/links.html#downloading-resources
Browser support can be checked at http://caniuse.com/download
(As for now, 2013, no IE nor Safari support)
I think, you can make a fallback for not-supporting browsers: use JS to change value of href="..." to the URL of your server script (which will return the file contents with appropriate HTTP header Content-disposition: attachment;filename=filename.txt).
Here is a pure Javascript solution for creating a text blob and download as text file
var fileContent = 'This is sample text file';
var fileName = 'sampleFile.txt';
const blob = new Blob([fileContent], { type: 'text/plain' });
const a = document.createElement('a');
a.setAttribute('download', fileName);
a.setAttribute('href', window.URL.createObjectURL(blob));
a.click();
What I did was sending the data to a server, which sends them back with the following HTTP header:
Content-disposition: attachment;filename=test.txt
I don't like this, but it works rather well.
This works as hell ...
<div class="tags-style-one dragme" draggable="true" data-transfer="33343">some value is 33343</div>
<script type="text/javascript">
(function ($) {
$(document).ready(function () {
$('.dragme').on("dragstart",function(evt) {
evt.originalEvent
.dataTransfer
.setData(
"text/plain",
$(this).data('transfer').toString()
);
});
})(jQuery);
</script>

Resources