I'm using fabric.js to draw annotations on page. Now I want to save anootated page as is, rather than to redraw all elements on server side using JSON.
I have main image loaded as:
function redrawPage(src) {
var deferred = $q.defer();
fabric.Image.fromURL(src, function (img) {
zoom.reset();
transformation.reset();
mainImage = img;
mainImage.set({
left: 0,
top: 0
});
mainImage.hasRotatingPoint = true;
mainImage.selectable = false;
canvas.clear();
canvas.setWidth(mainImage.getWidth());
canvas.setHeight(mainImage.getHeight());
canvas.add(mainImage);
canvas.renderAll();
deferred.resolve();
});
return deferred.promise;
}
and when I want to send canvas image data to be stored as annotated version of original image, I get "Operation is insecure" error.
function getImageData() {
var context = canvas.getContext('2d'),
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
return imageData.data;
}
web server from which I load images is not allowing crossOrigin set to "Anonymus"
If the image host does not allow anonymous access then your .getImageData & .toDataURL will always fail because the canvas is tainted. No enduring workaround for that.
You can copy (or re-route) the image to your own server and deliver it from the same domain as your web page. This satisfies cross-origin restrictions so your canvas will not be tainted and your .getImageData will succeed. Of course, copyright laws apply.
There are several other workarounds that involve the user confirming that they want the image to be loaded in a cross-origin compliant way. Here's a relevant Stackoverflow post.
Related
I'm implementing file download using AngularJS and WCF. My back-end is a .NET project hosted in IIS. The file is serialized as an array of bytes and then on the client side I utilize the File API to save the content.
To simplify the problem, back-end is like:
[WebInvoke(Method = "GET", UriTemplate = "FileService?path={path}")]
[OperationContract]
public byte[] DownloadFileBaseOnPath(string path)
{
using (var memoryStream = new MemoryStream())
{
var fileStream = File.OpenRead(path);
fileStream.CopyTo(memoryStream);
fileStream.Close();
WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = "attachment; filename=\"Whatever\"";
WebOperationContext.Current.OutgoingResponse.ContentType = "application/octet-stream"; // treat all files as binary file
return memoryStream.ToArray();
}
}
And on client side, it just sends a GET request to get those bytes, converts in into a blob and save it.
function sendGetReq(url, config) {
return $http.get(url, config).then(function(response) {
return response.data;
});
}
Save the file then:
function SaveFile(url) {
var downloadRequest = sendGetReq(url);
downloadRequest.then(function(data){
var aLink = document.createElement('a');
var byteArray = new Uint8Array(data);
var blob = new Blob([byteArray], { type: 'application/octet-stream'});
var downloadUrl = URL.createObjectURL(blob);
aLink.setAttribute('href', downloadUrl);
aLink.setAttribute('download', fileNameDoesNotMatter);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', false, false);
aLink.dispatchEvent(event);
}
else {
aLink.click();
}
setTimeout(function () {
URL.revokeObjectURL(downloadUrl);
}, 1000); // cleanup
});
}
This approach works fine with small files. I could successfully download files up to 64MB. But when I try to download a file larger than 64MB, the response.body is empty in Chrome. I also used Fiddler to capture the traffic. According to Fiddler, Back-end has successfully serialized the byte array and returned it. Please refer to the screenshot below.
In this example, I was trying to download a 70MB file:
And the response.data is empty:
Any idea why this is empty for file over 70MB? Though the response itself is more than 200MB, I do have enough memory for that.
Regarding to the WCF back-end, I know I should use Stream Mode when it comes to large files. But the typical use of my application is to download files less than 10MB. So I hope to figure this out first.
Thanks
Answer my own question.
Honestly I don't know what's going wrong. The issue still persists if I transfer it as a byte array. I eventually gave up this approach by returning a stream instead. Then on the client side, adding the following configuration
{responseType : blob}
and save it as a blob.
We are printing labels, which are created by a third party. We serve them directly, passing the base64 data into a blank window, so the user can print it himself.
No matter how many ways I try it, I cannot get the image itself to display.
Here's an excerpt of the data returned from the server (in any other display, it is full of � characters):
PNG
IHDR,Â_gbásRGB®ÎégAMA±üa pHYs88?'õÿ¥IDATx^ì½ ÅöÏî÷íþ÷Û½×T$$1`ÎY130b# ÂQ#%T8äs<d1¢sÆ|õÞëÝ÷_ÏÛSÓÍP}fúLO'ßßÝg'õtWÕTc=ç·ª 3*Ub%$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmâü«$ÓÍí$×MôqÝn!þîmü 4¥B!ÿ* ü¼¹£Fë&#ú¸n7 L÷¶I~P©R1«PÈ¿Jà?oî¨äº >n£ÛÂM ÄÓ½mWb
ùWI<àçÍ5\7AÒÇmt[¸Iøcº·MòBIYêIÀÈ¿JàÜÜq!Éu }ÜF·A?¦{Û$/U*¢JÅ¥¤þ¯ 9+È¿JàÜÜq!Éu }ÜF·A?¦{Û$O¨ÿ+¢bþn1D]
xÁº¹cBë&#ú¸n7 L÷¶I!bÎ[)FÔ¥¨s)Ô1ùWI<ßÍm\7AÒÇmt[¸Iøcº·Mª(ð&0(±BX%*bÙSÆüDþUä{sG$×MôqÝn!þîm¼ M
¢)ªTR¥¤¨OÃt1Ⱥøûïü«$¨ÈÍ\7AÒÇmt[¸Iøcº·Mò6)0$¼q)ö^1Íó7Ä"ÿ* *rsÇ$×MôqÝn!þîm¼` ×SuI=
¹¹ãBë&#ú¸n7 L÷¶IÙ#ü$W
ùWI<ëÍG\7AÒÇmt[¸Iøcº·MÊOËQBþUäzsÇ$×MôqÝn!þîm²i`¹ªPÈ¿Jà\oî8äº >n£ÛÂM ÄÓ½mRÁqº¹½äº >n£ÛÂM ÄÓ½mR6L¹*n*ò¯ x ×;$¹n¤Ûè¶p ñÇto
S®
ü«$Èõæ#I® éã
etc. etc.
This is what I'm trying to do with it - with various false starts:
vm.getLabelBinaryData = function (labelId){
labelsService.getShippingLabel(labelId).then (
function success(response) {
vm.openWindowForLabels(response.data);
},
function fail(response) {
toastr.error(response.data);
}
);
};
vm.openWindowForLabels = function (responseData) {
//var b64Response = btoa(rawResponse)
var image = new Image();
image.src = 'data:image/png;base64,' + responseData;
var popupWin = window.open('', '_blank', 'width=1000,height=1000');
popupWin.document.open();
popupWin.document.write('<html><head></head><body></body></html>');
popupWin.document.body.appendChild(image);
//popupWin.document.close();
};
When I inspect the HTML, I see this:
<html><head></head><body><img src="data:image/png;base64,�PNG
Strangely, in my browser window, I see the closing img bracket, and closing body and html tags. The fact that it didn;t copy leads em to believe the data is either to long 'too long to edit', or some special character is killing it before it displays.
Either way, I get a broken image icon.
I'm stumped.
UPDATE:
I found a sample here:
https://en.wikipedia.org/wiki/Data_URI_scheme
so I plugged than in to my code:
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
...and it indeed shows the red dot just fine.
This strongly suggests that my code is working fine and that the problems lies with parsing the data properly. (I have verified with the API dev that he is seeing a working image in POSTMAN.)
A server side application requires authorization on file download links. This means a normal <a ng-href="{{createLinkToFile()}}"> is no longer sufficient to get enough parameters passed to the server.
When trying to use a programmatic call to the file download, I get the response data back to Dart client application. Using a simple http GET:
var url = "http://example.com/file";
headers.putIfAbsent("Authorization", () => "bearer " + token;
_http.get(url: url, headers : headers);
The future returned by the GET will hold the data, but how do I instruct the browser to download it as a file, instead of just trying to keep it in memory?
Or is there a way to just do it in a normal link?
After downloading the data from the server like shown in Using Dart to Download a PNG File (Binary File) and displaying it not working you can create a download link like shown at http://blog.butlermatt.me/2014/03/dynamically-generating-download-files/
import 'dart:html';
void main() {
List body = [ 'Some test data ...\n'];
// Create a new blob from the data.
Blob blob = new Blob(body, 'text/plain', 'native');
// Create a data:url which points to that data.
String url = Url.createObjectUrlFromBlob(blob);
// Create a link to navigate to that data and download it.
AnchorElement link = new AnchorElement()
..href = url
..download = 'random_file.txt'
..text = 'Download Now!';
// Insert the link into the DOM.
var p = querySelector('#text');
p.append(link);
}
The code of Seth solves indeed part of the problem. To make it a bit more complete, I'm now using the following:
void doPdfFileRequest(String url) {
var request = new HttpRequest();
request.open('GET', url);
request.responseType = "blob";
request.withCredentials = false;
request.setRequestHeader("Accept", _httpAcceptHeader);
request.setRequestHeader("Authorization", "bearer " + token);
request.onReadyStateChange
.listen((r) => onData(request, "filename.pdf"));
request.send();
}
void onData(HttpRequest request, String filename) {
if (request.readyState == HttpRequest.DONE && request.status == 200) {
if (!isIE()) {
var contentType = request.getResponseHeader("content-type");
AnchorElement downloadLink = new AnchorElement(
href: Url.createObjectUrlFromBlob(request.response));
downloadLink.rel = contentType;
downloadLink.download = filename;
var event = new MouseEvent("click", view: window, cancelable: false);
downloadLink.dispatchEvent(event);
} else {
var href = Url.createObjectUrlFromBlob(request.response);
window.open(href, "_self");
}
}
}
A few things to notice. Instead of using the downloadLink.click(), a mouse event is constructed to ensure that it works on Firefox as well as on Safari and Chrome. Firefox seems not to handle the click() otherwise. Binding it to the DOM as is done in the code of Seth isn't necessary.
Internet Explorer doesn't understand the download attribute, so nothing will happen, therefore a window.open is used to at least have it work (though not ideal) on IE, it's redirecting to self to avoid being hit by the pop up blocker.
There are solutions that convert the result download result to Base64 first and put it in a data:mimetype href, using the blob this isn't necessary.
A nice way to set the filename on the file to download would be through the content disposition header, but this header is marked as unsafe, so cannot be used. The filename is now set in the code.
Another note, notice that a HttpRequest is used instead http.get(), The HttpRequest allows you to set the responseType, in this case blob, which can be transformed into a object url.
I am using Angular js to show loading screen. It works for all the REST services call except REST service to download the file. I understand why it is not working because for download I am not making any service call using $resource; instead of that I am using normal approach to download the file therefore Angular js code doesn't have any control on start/finish the service request. I tried to use $resource to hit this REST service however I am getting the data from this service and in this case loading screen was working fine however not sure how to use this data to display to user to download in angular way. Following are required details. Please help.
Approach 1 using iframe approach:
/*Download file */
scope.downloadFile = function (fileId) {
//Show loading screen. (Somehow it is not working)
scope.loadingProjectFiles=true;
var fileDownloadURL = "/api/files/" + fileId + "/download";
downloadURL(fileDownloadURL);
//Hide loading screen
scope.loadingProjectFiles=false;
};
var $idown; // Keep it outside of the function, so it's initialized once.
var downloadURL = function (url) {
if ($idown) {
$idown.attr('src', url);
} else {
$idown = $('<iframe>', { id: 'idown', src: url }).hide().appendTo('body');
}
};
Approach 2 using $resource (Not sure how to display data on screen to download)
/*Download file */
scope.downloadFile = function (fileId) {
//Show loading screen (Here loading screen works).
scope.loadingProjectFiles=true;
//File download object
var fileDownloadObj = new DownloadFile();
//Make server call to create new File
fileDownloadObj.$get({ fileid: fileid }, function (response) {
//Q? How to use the response data to display on UI as download popup
//Hide loading screen
scope.loadingProjectFiles=false;
});
};
This is the correct pattern with the $resource service:
scope.downloadFile = function (fileId) {
//Show loading screen (Here loading screen works).
scope.loadingProjectFiles=true;
var FileResource = $resource('/api/files/:idParam', {idParam:'#id'});
//Make server call to retrieve a file
var yourFile = FileResource.$get({ id: fileId }, function () {
//Now (inside this callback) the response data is loaded inside the yourFile variable
//I know it's an ugly pattern but that's what $resource is about...
DoSomethingWithYourFile(yourFile);
//Hide loading screen
scope.loadingProjectFiles=false;
});
};
I agree with you that this is a weird pattern and is different of other APIs where the downloaded data is assigned to a parameter in a callback function, hence your confusion.
Pay attention to the names and the cases of the parameters, and look that there're two mappings involved here, one between the caller to the $resource object and the object itself, and another between this object and the url that it contructs for downloading the actual data.
Here are some idea's for the second approach, you could present the user with a link after the download has happened:
With a "data url". Probably not a good idea for large files.
With a URL like "filesystem:mydownload.zip" You'd first have to save the file with the filesystem API. You can find some inspiration on html5rocks
I am trying to get contents of http://www.yahoo.com using WebClient#DownloadStringAsync(). However as Silverlight doesn't allow cross domain calls i am getting TargetInvocationException. I know we have to put clientaccesspolicy.xml and crossdomain.xml in our web server root but that is possible only if i have control on my services. Currently Google is not under my control ;), so how do i handle it?
I've did a workaround by making a WCF service in my web application and then calling WebClient. This works perfectly but it is rather ineffecient. Is there any other better way than this?
Thanks in advance :)
Silverlight's cross domain restricitions cause many developers to implement workarounds. If you need to display the html page you get back you should look into Silverlight 4 (WebBrowser) control although this only seems to work when running out-of-browser mode.
If you need to parse through the content you can try some of the following:
For a managed code solution the proxy service you have already implemented is your best option.
Write a Java applet that returns this information. Silverlight can interopt to javascript which can interopt into Java applets. This also works in the reverse but a little difficult to setup. (If you need more info on this let me know).
Use javascript XmlHttpRequest to get the data you want from the source. This can be difficult when supporting multiple browsers. This link shows an example of how to do this (you will need to scroll down). Javascript get Html
Code:
var xmlHttpRequestHandler = new Object();
var requestObject;
xmlHttpRequestHandler.createXmlHttpRequest = function(){
var XmlHttpRequestObject;
if(typeof XMLHttpRequest != "undefined")
{
XmlHttpRequestObject = new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
var tryPossibleVersions =["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp"];
for(i=0;i<tryPossibleVersions.length;i++)
{
try
{
XmlHttpRequestObject = new ActiveXObject(tryPossibleVersions[i]);
break;
}
catch(xmlHttpRequestObjectError)
{
// Ignore Exception
}
}
}
return XmlHttpRequestObject;}
function getHtml(){
var url = document.getElementById('url').value;
if(url.length > 0)
{
requestObject = xmlHttpRequestHandler.createXmlHttpRequest();
requestObject.onreadystatechange=onReadyStateChangeResponse;
requestObject.open("Get",url, true);
requestObject.send(null);
}}
function onReadyStateChangeResponse(){
var ready, status;
try
{
ready = requestObject.readyState;
status = requestObject.status;
}
catch(e) {}
if(ready == 4 && status == 200)
{
alert(requestObject.responseText);
}}