I want to create and save file before I log data into it. The method below is creating and saving data to file and it is only supported by Chrome browser. How can I create and save blank file and then log data into it and has IE and Chrome support?
ctrl.js:
function download(text, name, type) {
var a = document.getElementById("a");
var file = new Blob([text], {type: type});
a.href = URL.createObjectURL(file);
a.download = name;
}
$scope.recordLogs = function(){
download('file text', 'myfilename.txt', 'text/plain')
}
Save to filesystem
Have a look at angular-file-saver
Or use the following code as a reference in saving a BLOB. Where the blob object is generated from a JSON Object. But extration to a TEXT file is also possible.
// export page definition to json file
$scope.exportToFile = function(){
var filename = 'filename'
var blob = new Blob([angular.toJson(object, true)], {type: 'text/plain'});
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, filename);
} else{
var e = document.createEvent('MouseEvents'),
a = document.createElement('a');
a.download = filename;
a.href = window.URL.createObjectURL(blob);
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
e.initEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
// window.URL.revokeObjectURL(a.href); // clean the url.createObjectURL resource
}
}
Using LocalStorage
Saving to localStorage:
window.localStorage.setItem('key', value);
Getting from localStorage
window.localStorage.getItem('key');
Delete key from localStorage
window.localStorage.removeItem('key');
Or using the AngularJS module 'ngStorage'
Browser compatibility
Chrome - 4
Firefox (Gecko) - 3.5
Internet Explorer - 8
Opera - 10.50
Safari (WebKit) - 4
See live example (credits to #cOlz)
https://codepen.io/gMohrin/pen/YZqgQW
$http({
method : 'GET',
url : $scope.BASEURL + 'file-download?fileType='+$scope.selectedFile,
responseType: 'arraybuffer',
headers : {
'Content-Type' : 'application/json'
}
}).success(function(data, status, headers, config) {
// TODO when WS success
var file = new Blob([ data ], {
type : 'application/json'
});
//trick to download store a file having its URL
var fileURL = URL.createObjectURL(file);
var a = document.createElement('a');
a.href = fileURL;
a.target = '_blank';
a.download = $scope.selectedFile+'.json';
document.body.appendChild(a);
a.click();
}).error(function(data, status, headers, config) {
});
In success part need to open local system, by which the user can choose, where to save file. Here I have used <a>. And I am hitting restful service
How to Download Files Locally with AngularJS (with DEMO)
Use an <a> tag with a download attribute:
<a download="{{files[0].name}}" xd-href="data">
<button>Download data</button>
</a>
The xd-href Directive:
app.directive("xdHref", function() {
return function linkFn (scope, elem, attrs) {
scope.$watch(attrs.xdHref, function(newVal) {
newVal && elem.attr("href", newVal);
});
};
});
When downloading, browsers prompt user with a dialog that can be accepted or cancelled. For more information, see MDN HTML Reference - <a> Tag
THE DEMO
angular.module("app",[])
.controller("myVm", function($scope, $http, $window) {
var vm = $scope;
var url = "//httpbin.org/post";
var config = { headers: {"Content-Type": undefined} };
vm.upload = function() {
vm.spin = "Uploading...";
$http.post(url, vm.files[0], config).
then(function(response) {
vm.result = "SUCCESS";
vm.data = response.data.data;
}).catch(function(response) {
vm.result = "ERROR "+response.status;
}).finally(function() {
vm.spin = undefined
});
};
})
.directive("xdHref", function() {
return function linkFn (scope, elem, attrs) {
scope.$watch(attrs.xdHref, function(newVal) {
newVal && elem.attr("href", newVal);
});
};
})
.directive("selectNgFiles", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
});
}
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="myVm">
<h2>Upload and Download File with AngularJS</h2>
<input type="file" select-ng-files ng-model="files">
<br>
<code>
Name: {{files[0].name}}<br>
Size: {{files[0].size}}<br>
Type: {{files[0].type}}<br>
Date: {{files[0].lastModifiedDate}}<br>
</code>
<button ng-click="upload()" ng-disabled="!files">
Upload
</button>
<span ng-show="spin">{{spin}}</span>
<span ng-show="result">{{result}}</span>
<a download="data_{{files[0].name}}" xd-href="data">
<button ng-disabled="!data">Download data</button>
</a>
</body>
See also ng-model for <input type=“file”/> (with directive DEMO)
Related
In Safari, the canvas.toBlob function converts the image to png instead of webp.
var myDropzone = new Dropzone("div#myDropzone", {
url: thisForm.getAttribute('action'),
paramName: function() { return 'mainFileUploader'; },
autoProcessQueue: false,
//createImageThumbnails: true, // this needs testing
//thumbnailWidth: 120, // this needs testing
//thumbnailHeight: 120, // this needs testing
//thumbnailMethod: "crop", // how to scale down the thumbnail if image not square
uploadMultiple: true,
parallelUploads: 4,
maxFiles: 4,
maxFilesize: 40000000, // MB
acceptedFiles: 'image/*',
addRemoveLinks: true,
init: function() {
var myDropzone = this;
//Populate any existing thumbnails
if (photos) {
let photosNumber = photos.length
for (var i = 0; i < photosNumber; i++) {
var mockFile = {
name: photos[i],
size: 1000000,
type: 'image/webp',
status: Dropzone.ADDED,
accepted: true,
url: photos[i]
}
myDropzone.emit('addedfile', mockFile) // Call the default addedfile event handler
myDropzone.emit('thumbnail', mockFile, `/profiles/${goDatingCookie.userId}/photos/${photos[i]}`) // Show the thumbnail of the file
myDropzone.files.push(mockFile)
}
}
this.on("removedfile", function (file) {
if (file.url && file.url.trim().length > 0) { // Only files that have been programmatically added should have a url property.
let deletedPhotos = document.getElementById('deletedPhotos')
const fileName = file.url.replace(`/profiles/${goDatingCookie.userId}/photos/`, '')
if (deletedPhotos.value.length > 0) {
deletedPhotos.value = deletedPhotos.value + ',' + fileName
} else {
deletedPhotos.value = fileName
}
}
})
// for Dropzone to process the queue (instead of default form behavior):
const thisForm = document.forms['profileForm']
thisForm.addEventListener('submit', async function(e) {
e.preventDefault()
e.stopPropagation()
if (myDropzone.getQueuedFiles().length == 0) {
if (goDatingCookie.hasProfile) {
thisForm.append("lookingForReligions", getMultiSelect("lookingForReligions"))
thisForm.append("lookingForCountries", getMultiSelect("lookingForCountries"))
let formData = new FormData(thisForm)
const response = await getAndPostData({
url: thisForm.getAttribute('action'),
method: thisForm.getAttribute('method'),
enctype: thisForm.getAttribute('enctype'),
data: formData
})
if (response == HTTP204NoContent) {
location.hash = "#viewMyProfile"
}
} else {
new SystemMessage("You require a photo to create a profile.")
}
} else {
myDropzone.processQueue()
}
});
//send all the form data along with the files:
this.on("sendingmultiple", function(data, xhr, formData) {
formData.append('nickname', thisForm.elements.nickname.value)
formData.append('dateOfBirth', thisForm.elements.dateOfBirth.value)
formData.append('description', thisForm.elements.description.value)
formData.append('gender', thisForm.elements.gender.value)
formData.append('sexuality', thisForm.elements.sexuality.value)
formData.append('alcohol', thisForm.elements.alcohol.value)
formData.append('smoke', thisForm.elements.smoke.value)
formData.append('religion', thisForm.elements.religion.value)
formData.append('country', thisForm.elements.country.value)
formData.append('location', thisForm.elements.location.value)
formData.append('interests', thisForm.elements.interests.value)
formData.append('lookingForFromAge', thisForm.elements.lookingForFromAge.value)
formData.append('lookingForToAge', thisForm.elements.lookingForToAge.value)
formData.append('deletedPhotos', thisForm.elements.deletedPhotos.value)
formData.append("lookingForReligions", getMultiSelect("lookingForReligions"))
formData.append("lookingForCountries", getMultiSelect("lookingForCountries"))
});
this.on("successmultiple", function(files, response) {
goDatingCookie = JSON.parse(getCookie('goDatingCookie'))
location.hash = '#viewMyProfile'
});
this.on("errormultiple", function(files, response) {
new SystemMessage("There were problems uploading your profile.")
})
},
transformFile: function(file, done) {
// Create Dropzone reference for use in confirm button click handler
var myDropzone = this
// Create the image editor overlay
var editor = document.createElement('div');
editor.style.position = 'fixed';
editor.style.left = 0;
editor.style.right = 0;
editor.style.top = 0;
editor.style.bottom = 0;
editor.style.zIndex = 9999;
editor.style.backgroundColor = '#000';
document.body.appendChild(editor);
// Create confirm button at the top left of the viewport
var buttonConfirm = document.createElement('button');
buttonConfirm.style.position = 'absolute';
buttonConfirm.style.left = '50%';
buttonConfirm.style.top = '10px';
buttonConfirm.style.zIndex = 9999;
buttonConfirm.classList = "btn btn-godating btn-lg mt-4 rounded-pill"
buttonConfirm.textContent = 'Confirm';
editor.appendChild(buttonConfirm);
buttonConfirm.addEventListener('click', function() {
// Get the canvas with image data from Cropper.js
var canvas = cropper.getCroppedCanvas({
width: 1200,
height: 1200,
imageSmoothingEnabled: true,
imageSmoothingQuality: 'high'
});
// Turn the canvas into a Blob (file object without a name)
canvas.toBlob(function(blob) {
// Create a new Dropzone file thumbnail
myDropzone.createThumbnail(
blob,
myDropzone.options.thumbnailWidth,
myDropzone.options.thumbnailHeight,
myDropzone.options.thumbnailMethod,
false,
function(dataURL) {
// Update the Dropzone file thumbnail
myDropzone.emit('thumbnail', file, dataURL);
// Return the file to Dropzone
done(blob);
});
}, 'image/webp', 0.80);
// Remove the editor from the view
document.body.removeChild(editor)
});
// Create an image node for Cropper.js
var image = new Image();
image.src = URL.createObjectURL(file);
editor.appendChild(image);
// Create Cropper.js
var cropper = new Cropper(image, { viewMode: 1, autoCropArea: 1, aspectRatio: 1 });
}
});
On the receiving server it reports the following about the file:
fileFilter {
fieldname: 'mainFileUploader',
originalname: 'Me - Profile.jpg',
encoding: '7bit',
mimetype: 'image/png'
}
This is the same for Safari on the Apple Mac or iPhone.
On Chrome or Firefox on my Apple Mac it works perfectly and the receiving server reports:
fileFilter {
fieldname: 'mainFileUploader',
originalname: 'Me - Profile.jpg',
encoding: '7bit',
mimetype: 'image/webp'
}
Is there anyway of getting Safari to behave or do I just need to use jpeg files or convert the incorrectly converted png on the server side to webp?
Now, I am using plupload with my ASP Net MVC application. I'm stuck on sending data to the server (my Controller),
the code as follows:
Thank you for your help;
var blob = "";
var fileObject = new File([blob], fileName, {
type: 'video/mp4'
});
var uploader = new plupload.Uploader({
runtimes: 'html5,html4',
url: "/videoUploadHandler.ashx",
chunk_size: '5mb',
multi_selection: true,
unique_names: false,
filters: {
mime_types: [
{ title: "video files", extensions: "avi,mp4,mov,mwm,mkv" }
]
}
});
uploader.init();
uploader.files.push(fileObject);
uploader.start();
videoUploadHandler.ashx
......................
HttpPostedFile fileUpload = Request.Files[0];
string fileName = fileUpload.FileName;
var path = FileUploadPhysicalPath;
uploadedFilename = Path.GetFileName(uploadedFilename);
string uploadFilePath = Path.Combine(path, uploadedFilename);
Stream stream = null;
stream = new FileStream(uploadFilePath, (chunk == 0) ? FileMode.CreateNew : FileMode.Append);
chunkStream.CopyTo(stream, 16384);
I am getting this response from the server for a jpeg image get request(with CORS):
ÿØÿàJFIFÿÛ ( %!1"%)+.............
After converting this to base64 it using btoa(encodeURIComponent(data)) looks like:
data:image/jpeg;base64,JUVGJUJGJUJEJUVGJUJGJUJEJUVGJUJGJUJEJUVGJUJGJUJEJTAwJTEwSkZJRiUwMCUwMSUwMSUwMSUwMEglMDBIJTAwJTAwJUVGJUJGJUJEJUVGJUJGJUJEJTAwJUVGJUJGJUJERXhpZiUwMCUwME1NJTAwKiUwMCUwMCUwMCUwOCUwMCUw...............................
On setting the img tag src attribute to the base64 above the image is not rendering in the browser.
However, when I open the image link in the browser tab, it loads properly. Also, on sending the same request in postman it renders the image in the response, in postman.
Even using the blob approach doesn't work (used bower: angular-img-http-src)
$scope.$watch('objectURL', function (objectURL) {
elem.attr('src', objectURL);
});
$scope.$on('$destroy', function () {
revokeObjectURL();
});
$http.get(url)
.then(function (response) {
var blob = new Blob(
[ response.data ],
{ type: response.headers('Content-Type') }
);
$scope.objectURL = URL.createObjectURL(blob);
});
Kindly help here.
In Service:
yourService : function(options) {
return $http.get(url, {
responseType : 'arraybuffer'
}
).success(function(data) {
var file = new Blob([ data ], {
type : 'image/jpeg'
});
var fileURL = URL.createObjectURL(file);
if(options && options.successCallBack) {
return options.successCallBack(fileURL, {});
}
});
},
In Controller:
function yourImageSuccessHandler(fileUrl, options) {
$scope.objectUrl= fileUrl; // now you will have fileUrl in
// controller
}
yourService.getDownloadDoc(empId, {
successCallBack: yourImageSuccessHandler
});
In template HTML:
<img ng-src={{objectUrl}}></img>
Though I don't know if there's a specific angular routine, the general JS solution for created images goes something like this...
function addIMG(durl, callback) {
var img = new Image();
img.addEventListener('load', function() {
callback(this);
}, false);
img.src = durl;
}
addIMG(objectURL, function(img) {
$('#element').appendChild(img);
});
Hope that helped :)
I'm using ng-click to call a function which makes a post http request to the server and then creates a link. How can I use this created link to also download the file attached to it?
My template
<button ng-click="getFile(row)">Download</button>
My controller
$scope.getFile = function(row){
row.isSelected = true;
var link = null;
var postData = {
"data" : {
"type": "member_report",
"relationships": {
"member" : {
"data": {
"type": "user",
"id": memberID
}
}
}
}
}
ajaxRequest.ajaxPost('http://someApi.com', postData).then(
function(jsonAPI) {
link = jsonAPI.links.download; //here is the response link
//todo something with it to download file
},
function(errorResponse) {
}
);
}
By the way ajaxRequest is just a simple $http service wrapper.
If I understood you, then I suppose that you want to initiate the download as soon as you get the link dynamically, then you can proceed as follows
$scope.getFile = function(row){
row.isSelected = true;
var link = null;
var postData = {
"data" : {
"type": "member_report",
"relationships": {
"member" : {
"data": {
"type": "user",
"id": memberID
}
}
}
}
}
ajaxRequest.ajaxPost('http://someApi.com', postData).then(
function(jsonAPI) {
link = jsonAPI.links.download;
// Now we want to download the link
var downloadLink = document.createElement('a');
downloadLink .href = link;
// now set the visibility to hidden so that it doesnt effect the frontend layout
downloadLink .style = 'visibility:hidden';
downloadLink .download = 'file_name';
// now append it to the document, generate click and remove the link
document.body.appendChild(downloadLink );
downloadLink .click();
document.body.removeChild(downloadLink );
},
function(errorResponse) {
}
);
}
Try to save the link in the the $scope. Then, use this:
<a target="_self" href={{your variable}} download="foo.pdf">
Also check the documentation:
http://docs.angularjs.org/guide/
Answer taken from here:
How do you serve a file for download with AngularJS or Javascript?
I managed to do it using the $window service.
ajaxRequest.ajaxPost('http://someApi.com', postData).then(
function(jsonAPI) {
link = jsonAPI.links.download;
$window.location.href = link;
},
function(errorResponse) {
}
);
Just had to add $window as a dependency
I am not far from it to get the file upload working with Ember-data. But I do not get the value binding right. Below the relevant code.
This is the App.js
App.LandcodeNewRoute = Ember.Route.extend({
model: function () {
return this.store.createRecord('landcode');
},
actions: {
saveLandcode: function () {
this.currentModel.save();
}
}
});
// REST & Model
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
App.Store = DS.Store.extend({
adapter: 'App.ApplicationAdapter'
});
App.Landcode = DS.Model.extend({
code: DS.attr('string'),
image: DS.attr('string')
});
// Views
App.UploadFile = Ember.TextField.extend({
tagName: 'input',
attributeBindings: ['name'],
type: 'file',
change: function (e) {
var reader, that;
that = this;
reader = new FileReader();
reader.onload = function (e) {
var fileToUpload = e.target.result;
console.log(e.target.result); // this spams the console with the image content
console.log(that.get('controller')); // output: Class {imageBinding: Binding,
that.get('controller').set(that.get('name'), fileToUpload);
};
return reader.readAsText(e.target.files[0]);
}
});
HTML
<script type="text/x-handlebars" data-template-name="landcode/new">
Code: {{input value=code}}<br />
Image: {{view App.UploadFile name="image" imageBinding="Landcode.image" }}
<button {{action 'saveLandcode'}}>Save</button>
</script>
As you can see in the HTML part is that I try to bind the imagecontent to the Landcode model attribute image. Tried it also without capital L.
I think I cant bind the image as such, because it is a custom view object? And also normally it would bind automatically I think. Maybe I am just doing some things twice.
References:
http://emberjs.com/api/classes/Ember.Binding.html
http://devblog.hedtek.com/2012/04/brief-foray-into-html5-file-apis.html
File upload with Ember data
How: File Upload with ember.js
http://discuss.emberjs.com/t/file-uploads-is-there-a-better-solution/765
http://chrismeyers.org/2012/06/12/ember-js-handlebars-view-content-inheritance-image-upload-preview-view-object-binding/
I updated your code to the following:
App.LandcodeNewRoute = Ember.Route.extend({
model: function () {
return this.store.createRecord('landcode');
},
actions: {
saveLandcode: function () {
this.currentModel.save();
}
}
});
// REST & Model
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
App.Landcode = DS.Model.extend({
code: DS.attr('string'),
image: DS.attr('string')
});
// views
App.UploadFile = Ember.TextField.extend({
tagName: 'input',
attributeBindings: ['name'],
type: 'file',
file: null,
change: function (e) {
var reader = new FileReader(),
that = this;
reader.onload = function (e) {
var fileToUpload = e.target.result;
Ember.run(function() {
that.set('file', fileToUpload);
});
};
return reader.readAsDataURL(e.target.files[0]);
}
});
In the App.UploadFile instead of reference the controller directlly, I set the file property. So you can bind your model property, with the view using:
{{view App.UploadFile name="image" file=image }}
The Ember.run is used to you don't have problems when testing the app.
Please give a look in that jsfiddle http://jsfiddle.net/marciojunior/LxEsF/
Just fill the inputs and click in the save button. And you will see in the browser console, the data that will be send to the server.
I found that using attaching a data URI to a model attribute didn't allow files more than about 60k to be uploaded. Instead I ended up writing a form data adapter for ember-data. This will upload all the model data using FormData. Sorry that it's in CoffeeScript rather than JS but it's pasted from my blog post. You can read the whole thing at http://blog.mattbeedle.name/posts/file-uploads-in-ember-data/
`import ApplicationAdapter from 'appkit/adapters/application'`
get = Ember.get
FormDataAdapter = ApplicationAdapter.extend
ajaxOptions: (url, type, hash) ->
hash = hash || {}
hash.url = url
hash.type = type
hash.dataType = 'json'
hash.context = #
if hash.data and type != 'GET' and type != 'DELETE'
hash.processData = false
hash.contentType = false
fd = new FormData()
root = Object.keys(hash.data)[0]
for key in Object.keys(hash.data[root])
if hash.data[root][key]
fd.append("#{root}[#{key}]", hash.data[root][key])
hash.data = fd
headers = get(#, 'headers')
if headers != undefined
hash.beforeSend = (xhr) ->
for key in Ember.keys(headers)
xhr.setRequestHeader(key, headers[key])
hash
`export default FormDataAdapter`