Populate ng-flow with jpeg/jpg as blob - javascript

I'm currently trying to populate ng-flow with images from my webserver(nodejs http-server), and I found this thread:
Populating image files with ng-flow.js from json
As you can see in the answer from Aidas, he says that you need to add the data to a blob and then use addFile(blob)
But... when I use $resource to GET an url like:
http://localhost:8080/3c6397ff-cbb4-4a1c-98b3-5304e02c1bd4.jpg
and then take the value from $resource, and adding it to a blob the image is missing - In ng-flow it says that the blob is 15kb - and not the 700kb the actually image is.
I've read that the picture could be base64 formatted, but my google dev console says:
content-length:780831
content-type:image/jpeg; charset=utf-8
If I take a look at the response data in google dev console, there's alot of questionmarks (it cannot display the character I guess).
How do I get the response formatted correctly, so I can add it to ng-flow using the addFile(blob) function?

I did something combining cvjensen solution and penner's one in Populating image files with ng-flow.js from json
Controller:
First I read images from db and store them in $scope.project.images:
$scope.project.images : [
{
"image_type" : "image/jpeg",
"src" : "/images/uploaded/flow-122256-fontiosojpg.1",
"alt" : "fontioso.jpg",
"_id" : ObjectId("5608ef37f7672d8b1fcab111")
}
]
Then:
Flow.prototype.addExistingFile = function (file, event) {
var f = new Flow.FlowFile(this, file);
this.files.push(f);
};
angular.forEach($scope.project.images, function(value, key) {
getBase64Images.get(value.src) //src contains the full path to img
.success(function(data) {
var blob = new Blob([data.data], {type: value.image_type});
blob.name = value.alt;
blob.image_url = value.src;
blob.alt_text = value.alt;
$scope.uploader.flow.addExistingFile(blob);
})
.error(function(error){
console.log(error);
});
});
Service:
.factory("getBase64Images", ['$http', function ($http) {
return {
get: function (url) {
return $http.get(
url, {
responseType: 'arraybuffer',
transformResponse: function(data) {
console.log(data);
return { data: data };
}
}
);
}
};
}]);

I ended up doing like this..
My $resource function look like:
ENV.imagesEndpoint = 'http://localhost:8080/
id = the image name / ID
getimages: $resource(ENV.imagesEndpoint + id, {}, {
get: {
method: 'GET',
isArray: false,
responseType: 'arraybuffer',
transformResponse: function(data) {
// Stores the ArrayBuffer object in a property called "data"
return { data: data };
}
//headers: {'Content-Type': 'image/jpeg;charset=utf-8;base64'}
}
})
My controller look like:
angular.forEach($scope.images, function(imageid) {
filefactory(imageid).getimages.get().$promise.then(function(value) {
$timeout(function() {
var blob = new Blob([value.data], {type: 'image/jpeg'});
blob.name = 'file.jpg';
$scope.obj.flow.addFile(blob);
});
});
});

The solution I'll provide here is based on the post here:
Creating a Blob from a base64 string in JavaScript
I faced similar case, but the images are stored on the server using Base64.When the web page is loaded, and the images are retrieved from the Database, such images must be added back to the flow.files array. The images are saved in Database using Base64 string. So, during page load, the only way for me was to convert Base64 string to Blob and add the files back to the flow.files array.
This enabled the flow controller to function correctly after the page is loaded from the Database.
Following are the steps:
Add directive load-photo and add it to the input element additional_image1 which has the Base64 string loaded from Database on document ready event using jQuery.
Add a Directive to access the element and call scope function $scope.loadPhoto on document ready to load the photo.
In load photo function, convert the Base64 to Blob and add the file to the flow control.
Ensure the scope variable $scope.imageStringB64 and the input element additional_image1 are synchronized manually as ng-model didn't work as expected. This is because jQuery code outside angular is loading the input element from Database, and I found out that they are not bound dynamically.
JavaScript code:
app.directive('loadPhoto', function () {
return function (scope, element, attrs) {
//This directive 'load-photo' is required to access the DOM element inside the scope
//This will get the Base64 string of the image which is stored in the input element
angular.element(document).ready(function () {
scope.loadPhoto(element[0]);
})
}
})
app.controller('photosController', ['$scope', '$http', '$timeout',
function ($scope, $http, $timeout) {
...
var BLANK_IMG_URL = "//:0";
$scope.removeFile = function () {
//debugger;
$scope.$flow.cancel();
$scope.imageStringB64 = '';
$scope.imageString = BLANK_IMG_URL;
}
$scope.loadPhoto = function (elem) {
//Load photo from Database
//The photo Base64 is stored in the input element 'elem'
var blobImage;
var tmpBase64;
tmpBase64 = angular.element(elem).val();
if (tmpBase64) {
//Convert Base64 to Blob object
blobImage = b64toBlob(tmpBase64, 'image/png');
blobImage.name = "image.png";
//Add the Blob object to flow files.
$scope.$flow.addFile(blobImage);
}
}
...
}]);
HTML Code:
<div class="photo-wrapper" ng-controller="photosController"
flow-init
flow-file-added="!isAppraiserSigned() && !!{png:1,gif:1,jpg:1,jpeg:1}[$file.getExtension()]"
flow-files-submitted="$flow.upload()">
<h4 class='photo-title'>Photo 1</h4>
<div class="property-photo drag-drop-photo" ng-hide="$flow.files.length" flow-drop
flow-drag-enter="isAppraiserSigned()?style={}:style={border:'4px solid green'}" flow-drag-leave="style={}" ng-style="style">
<div class='drag-drop-lbl'>Drop file here</div>
</div>
<div class="property-photo" flow-drop ng-show="$flow.files.length"
flow-drag-enter="isAppraiserSigned()?style={}:style={border:'4px solid green'}" flow-drag-leave="style={}" ng-style="style">
<img id="additional_image1_section" style="max-height:100%" flow-img="$flow.files[0]" />
<input id="additional_image1" name = "additional_image1" ng-hide="true" type="text" ng-model="imageStringB64" load-photo/>
</div>
<div>
<%=selectImageLbl%>
Change
<a href="#" class="btn btn-danger" ng-show="$flow.files.length"
ng-click="removeFile()">
Remove
</a>
</div>
<div class='photo-description'>
<label class='description-lbl' for='additional_image_describe1'><%=descriptionLbl%></label>
<input class='description' id='additional_image_describe1' name='additional_image_describe1' type="text" />
</div>
</div>
See this code sample for more options to convert Base64 image to blob and back to Base64:
fiddle.jshell.ne

Related

Encoding a CSV file using javascript and passing the value to a 2nd Cloud Page not working as expected

What I am trying to do : I have a cloud page where the user can upload CSV file. When user clicks on the “upload” button the a function called getBase64() is called (please refer the below code). The getBase64() function will encode the uploaded file and post it to a second cloud page.The second cloud page then takes the posted data.
Note: I am trying to adapt this solution to my need (csv file) by referring to this article partially https://sfmarketing.cloud/2020/02/29/create-a-cloudpages-form-with-an-image-file-upload-option/
What’s the problem : When I try to click the the “upload” button the page is not taking me to the second CloudPage. Please could anyone let me know what I am doing wrong here ?
Here is the code:
CloudPage 1
<input id="file" type="file" accept=".csv">
<br>
<button id="button">Upload</button>
<script runat="client">
document.getElementById("button")
.addEventListener("click", function() {
var files = document.getElementById("file").files;
if (files.length > 0) {
getBase64(files[0]);
}
});
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
//prepare data to pass to processing page
var fileEncoded = reader.result;
var base64enc = fileEncoded.split(";base64,")[1];
var fullFileName = document.getElementById("file").files[0].name;
var fileName = fullFileName.split(".")[0];
var assetName = fullFileName.split(".")[1];
fetch("https://cloud.link.example.com/PAGE2", { //provide URL of the processing page
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
base64enc: base64enc,
fileName: fileName,
assetName: assetName
})
})
.then(function(res) {
window.alert("Success!");
})
.catch(function(err) {
window.alert("Error!");
});
};
reader.onerror = function(error) {
console.log('Error: ', error);
};
}
</script>
CloudPage 2
<script runat="server">
var jsonData = Platform.Request.GetPostData();
var obj = Platform.Function.ParseJSON(jsonData);
</script>
I do not see any errors in the code and when I click on the upload button I get a success message but it does not take me to the second page. Please can anyone guide me how to retrieve this posted data in second page as I am not able to get the encoded data in page 2?

Kendo Upload cannot get Byte array of selected file

How can take the byte array data of selected file? from select event i can get e.files[0].rawFile but i cannot find where is stored the byte[]. i want this data on client side
I found the documentation pretty poor on this matter as well. The documentation is geared at using a REST endpoint to do server side processing of the file. Most of the examples of client side processing are just for showing the file names, which isn't super useful if you want to do any meaningful client-side validation of the content of the file. In my case, I have CSV files I want to do some pre-processing on the client side. The trick is you need to use the HTML5 a FileReader. Here is an example in AngularJS where $ctrl.fileContent will have the content of a CSV file.
controller: ['$scope', function ($scope) {
var $ctrl = this;
// function called by kendo upload component
$ctrl.onSelect = function(e) {
var message = e.files.map(function(file) {
return $ctrl.readFile(file);
}).join(', ');
// log file names being uploaded
console.log('event :: files select (' + message + ')');
};
// read file and do basic validation
$ctrl.readFile = function(file) {
var reader = new FileReader();
reader.onload = function(e) {
var text = reader.result;
console.log('File contents :: ' + text);
$ctrl.fileContent = text;
};
reader.readAsText(file.rawFile);
// return file name
return file.name;
};
// function called by kendo upload component
$ctrl.onUpload = function(e) {
// todo
console.log('Uploading file');
console.log($ctrl.fileContent);
};
}]
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="content-panel">
<div class="row">
<div class="form-group col-lg-3 col-md-3 col-sm-6 col-xs-12">
<h4>Upload CSV files</h4>
<input name="files"
type="file"
kendo-upload
k-async="{ saveUrl: '$ctrl.save', removeUrl: 'remove', autoUpload: false }"
k-select="$ctrl.onSelect"
k-validation="{ allowedExtensions: ['csv']}"
k-upload="$ctrl.onUpload"
/>
</div>
</div>
</div>
try something like this:
function(data) {
data = btoa(data);
var bytesArr = [];
for(var i = 0; i < data.length; i++) {
bytesArr.push(data.charCodeAt(i));
}
return new Uint8Array(bytesArr);
}

Download a file from server and giving filename in angularjs

I am trying to download a zip file from my server (spring mvc controller).
here is my code in angularjs (1.5) controller to download zip file.
$http({
url: '/myurl',
method: 'GET',
headers: {
'Content-type': 'application/zip'
},
responseType: 'arraybuffer'
}).success(function (data,status,headers) {
var blob = new Blob([data], {type: "application/zip"});
var objectUrl = URL.createObjectURL(blob);
var file = headers('Content-Disposition');
window.open(objectUrl);
});
Above code works, but I need to give file name which I am getting in the response header. I got the file name from header('Content-Disposition') how to use this file name to downloaded file ? currently it is giving any random file name.
I tried below code it works in chrome but its not working in mozilla... is there any other solution which works in all browsers ?
//var anchor = document.createElement("a");
//anchor.download = "ATMOSLogFiles.zip";
//anchor.href = objectUrl;
//anchor.click();
Thanks for help !
An blob based solution:
You could use angular-file-saver to achieve this.
var app = angular.module('myApp', ['ngFileSaver'])
app.controller('ExampleCtrl', ['FileSaver', 'Blob', function () {
$scope.download = function () {
var myData = new Blob([text], { type: 'text/plain;charset=utf-8' });
FileSaver.saveAs(myData, 'text.txt');
}
}]);
An other solution based on HTML5:
A simple way by using the HTML5 download attribute / MDN documentation. No need for blobs. This attribute is supported by any browser & browser version which supports AngularJS (excluding IE10/IE11 - IE Edge does support it).
Download
The above answer by #lin is correct but I want to add that as the question demands, one can directly pass the file name set at the server to be the file name of the file at the client side as follows:
Just install the angular-file-saver, reference it in your app and inject it as a dependency.
var app = angular.module('myApp', ['ngFileSaver']);
app.controller('mainCtrl', ['FileSaver', 'Blob', '$http', '$scope', function(FileSaver, Blob, $http, $scope) {
// make ajax call to server
$scope.download = function() {
var req = {
url: 'your server url',
method: 'GET',
responseType: 'arraybuffer'
};
$http(req).then(function(resp){
var serverData = new Blob([resp.data], {type: resp.headers()['content-type']});
FileSaver.saveAs(serverData, resp.headers()['content-disposition']); // File name set at server passed to the FileSaver function
});
}
}]);

AngularJS image upload preview without directive

I'm uploading files via service:
var addFile = function(files) {
var deferred = $q.defer();
var fd = new FormData();
fd.append("file", files[0]);
$http.post("/files", fd, {
***
})
.success(function(data, status, headers, config) {
***
})
.error(function(err, status) {
***
});
***
};
and in controller I have something like:
uplService.addFile($scope.files).then(function(url) {
$scope.news.Photo = url;
});
and in HTML view:
<input type="file" name="file" onchange="angular.element(this).scope().photoChanged(this.files)" />
before that I uploaded file on-the-go, when I select file it goes directly to server, but now I need to display it in my form when I select it, but upload later, all I see in web is using directives, but how could I organize it without using directives?
Can you try this in your controller to pass your file object here:
$scope.fileReaderSupported = window.FileReader != null;
$scope.photoChanged = function(files){
if (files != null) {
var file = files[0];
if ($scope.fileReaderSupported && file.type.indexOf('image') > -1) {
$timeout(function() {
var fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function(e) {
$timeout(function(){
$scope.thumbnail.dataUrl = e.target.result;
});
}
});
}
}
};
and on the view
<img ng-show="thumbnail.dataUrl != null" ng-src="{{ thumbnail.dataUrl }}" class="thumb">
demo here
Hope this help
I read this article, which helped me to solve the problem about uploading the image.
If you want to show your selected file, try this:
<img data-ng-src="data:image/png;base64,{{news.Photo}}" id="photo-id"/>
Explanation:
Your property for image in Model/ViewModel/Class must be an array of bytes, like
public byte[] Photo { get; set; }
The data:image/jpeg;base64 defines the byte array from news.Photo so it can be rendered correctly on the clients browser.
The $scope.news.Photo in your case is just an scoped variable which contains the drawed image with byte created by the byte equivalent in the $scope.uploadFile function from article.
I hope it will be also helpful for you.

How to upload base64 image resource with dropzone?

I'm trying to upload generated client side documents (images for the moment) with Dropzone.js.
// .../init.js
var myDropzone = new Dropzone("form.dropzone", {
autoProcessQueue: true
});
Once the client have finished his job, he just have to click a save button which call the save function :
// .../save.js
function save(myDocument) {
var file = {
name: 'Test',
src: myDocument,
};
console.log(myDocument);
myDropzone.addFile(file);
}
The console.log() correctly return me the content of my document
data:image/png;base64,iVBORw0KGgoAAAANS...
At this point, we can see the progress bar uploading the document in the drop zone but the upload failed.
Here is my (standart dropzone) HTML form :
<form action="/upload" enctype="multipart/form-data" method="post" class="dropzone">
<div class="dz-default dz-message"><span>Drop files here to upload</span></div>
<div class="fallback">
<input name="file" type="file" />
</div>
</form>
I got a Symfony2 controller who receive the post request.
// Get request
$request = $this->get('request');
// Get files
$files = $request->files;
// Upload
$do = $service->upload($files);
Uploading from the dropzone (by drag and drop or click) is working and the uploads are successfull but using the myDropzone.addFile() function return me an empty object in my controller :
var_dump($files);
return
object(Symfony\Component\HttpFoundation\FileBag)#11 (1) {
["parameters":protected]=>
array(0) {
}
}
I think i don't setup correctly my var file in the save function.
I tryied to create JS image (var img = new Image() ...) but without any success.
Thanks for your help !
Finally i found a working solution without creating canvas :
function dataURItoBlob(dataURI) {
'use strict'
var byteString,
mimestring
if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
byteString = atob(dataURI.split(',')[1])
} else {
byteString = decodeURI(dataURI.split(',')[1])
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i)
}
return new Blob([new Uint8Array(content)], {type: mimestring});
}
And the save function :
function save(dataURI) {
var blob = dataURItoBlob(dataURI);
myDropzone.addFile(blob);
}
The file appears correctly in dropzone and is successfully uploaded.
I still have to work on the filename (my document is named "blob").
The dataURItoBlob function have been found here : Convert Data URI to File then append to FormData
[EDIT] : I finally wrote the function in dropzone to do this job. You can check it here : https://github.com/CasperArGh/dropzone
And you can use it like this :
var dataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmAAAAKwCAYAAA...';
myDropzone.addBlob(dataURI, 'test.png');
I can't comment currently and wanted to send this to you.
I know you found your answer, but I had some trouble using your Git code and reshaped it a little for my needs, but I am about 100% positive this will work for EVERY possible need to add a file or a blob or anything and be able to apply a name to it.
Dropzone.prototype.addFileName = function(file, name) {
file.name = name;
file.upload = {
progress: 0,
total: file.size,
bytesSent: 0
};
this.files.push(file);
file.status = Dropzone.ADDED;
this.emit("addedfile", file);
this._enqueueThumbnail(file);
return this.accept(file, (function(_this) {
return function(error) {
if (error) {
file.accepted = false;
_this._errorProcessing([file], error);
} else {
file.accepted = true;
if (_this.options.autoQueue) {
_this.enqueueFile(file);
}
}
return _this._updateMaxFilesReachedClass();
};
})(this));
};
If this is added to dropzone.js (I did just below the line with Dropzone.prototype.addFile = function(file) { potentially line 1110.
Works like a charm and used just the same as any other. myDropzone.addFileName(file,name)!
Hopefully someone finds this useful and doesn't need to recreate it!
1) You say that: "Once the client have finished his job, he just have to click a save button which call the save function:"
This implies that you set autoProcessQueue: false and intercept the button click, to execute the saveFile() function.
$("#submitButton").click(function(e) {
// let the event not bubble up
e.preventDefault();
e.stopPropagation();
// process the uploads
myDropzone.processQueue();
});
2) check form action
Check that your form action="/upload" is routed correctly to your SF controller & action.
3) Example Code
You may find a full example over at the official Wiki
4) Ok, thanks to your comments, i understood the question better:
"How can i save my base64 image resource with dropzone?"
You need to embedd the image content as value
// base64 data
var dataURL = canvas.toDataURL();
// insert the data into the form
document.getElementById('image').value = canvas.toDataURL('image/png');
//or jQ: $('#img').val(canvas.toDataURL("image/png"));
// trigger submit of the form
document.forms["form1"].submit();
You might run into trouble doing this and might need to set the "origin-clean" flag to "true". see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#security-with-canvas-elements
how to save html5 canvas to server

Categories

Resources