IE + XMLHttp + CreateObjectURL Error - javascript

I am trying to download and show the contents of a remote file inside an iFrame , and succeeded in all browsers except for IE(i am trying with IE 10).
I have used XMLHttpRequest,Blob,CreateOBjectUrl APIs to complete the process.
In IE i am not able to view the file content inside the iFrame and also no particular error messages appeared on console as well.
I had pasted my code at the bottom of this thread , and a step by step explanation as below
Getting the download document url & corresponding mime
type(Perfectly fine in all broswers).
Invoking XMLHttp Request , a
Http GET Async call ,as response type as 'arraybuffer' (Perfectly
fine in all browsers) Upon completing the XMLHttpGet below 3 steps are
executing.
Creating a blob using the proper mimetype ;(Perfectly fine in all other browsers, specially verified the blob by downloading it in IE using MSSaveOrOpenBlob method).
4.InOrder to bind the blob contents to the iFrame , create the blob url using "createObjectURL" (Perfectly fine in all browsers , but in IE we are not getting a perfect URL).
Finally binding the URL with the iFrame for display.
Code snippet below.
// Getting the document url and mime type ( Which is perfectly fine )
var downloadUrl=finalServerURL + "DocumentService.svc/GetItemBinary?id=" + itemId + "&version=" + version;
var mimeTypeForDownload = responseStore.mimeTypes[currentlySelectedObject.fileExtension];
window.URL = window.URL || window.webkitURL;
//Defining the XML Http Process
var xhr = new XMLHttpRequest();
xhr.open('GET', downloadUrl, true);
xhr.responseType = 'arraybuffer'; //Reading as array buffer .
xhr.onload = function (e) {
var mimeType = mimeTypeForDownload;
var blob = new Blob([xhr.response], { type: mimeType });
// Perfect blob, we are able to download it in both IE and non-IE browsers
//This below url from createObjectURL,
//Working perfectly fine in all non-IE browsers, but nothing happening in IE
var url = window.URL.createObjectURL(blob);
document.getElementById(documentContentiFrameId).setAttribute("src", url);
};
xhr.send;
Please let me if you get any information on this , would be really helpful.

I came to know that its not possible in IE to get a proper URL for your blob entries , none of my attempts are get succeeded.
My alternative solutions,
1)go for pdf.js , an open source javascript library , which allows to render pdf binaries and equivalent pdf blobs.
2)Write your own viewers by utilizing the open PDF libraries , which will be time consuming , and more learning efforts involved.
Thanks,
Vishnu

Related

How to open an ajax pdf callback on new tab or force download? [duplicate]

I am trying to build a PDF file out of a binary stream which I receive as a response from an Ajax request.
Via XmlHttpRequest I receive the following data:
%PDF-1.4....
.....
....hole data representing the file
....
%% EOF
What I tried so far was to embed my data via data:uri. Now, there's nothing wrong with it and it works fine. Unfortunately, it does not work in IE9 and Firefox. A possible reason may be that FF and IE9 have their problems with this usage of the data-uri.
Now, I'm looking for any solution that works for all browsers. Here's my code:
// responseText encoding
pdfText = $.base64.decode($.trim(pdfText));
// Now pdfText contains %PDF-1.4 ...... data...... %%EOF
var winlogicalname = "detailPDF";
var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+
'resizable,screenX=50,screenY=50,width=850,height=1050';
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
// Open PDF in new browser window
var detailWindow = window.open ("", winlogicalname, winparams);
detailWindow.document.write(htmlText);
detailWindow.document.close();
As I have said, it works fine with Opera and Chrome (Safari hasn't been tested). Using IE or FF will bring up a blank new window.
Is there any solution like building a PDF file on a file system
in order to let the user download it? I need the solution that works in all browsers, at least in IE, FF, Opera, Chrome and Safari.
I have no permission to edit the web-service implementation. So it had to be a solution at client-side. Any ideas?
Is there any solution like building a pdf file on file system in order
to let the user download it?
Try setting responseType of XMLHttpRequest to blob , substituting download attribute at a element for window.open to allow download of response from XMLHttpRequest as .pdf file
var request = new XMLHttpRequest();
request.open("GET", "/path/to/pdf", true);
request.responseType = "blob";
request.onload = function (e) {
if (this.status === 200) {
// `blob` response
console.log(this.response);
// create `objectURL` of `this.response` : `.pdf` as `Blob`
var file = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
a.href = file;
a.download = this.response.name || "detailPDF";
document.body.appendChild(a);
a.click();
// remove `a` following `Save As` dialog,
// `window` regains `focus`
window.onfocus = function () {
document.body.removeChild(a)
}
};
};
request.send();
I realize this is a rather old question, but here's the solution I came up with today:
doSomethingToRequestData().then(function(downloadedFile) {
// create a download anchor tag
var downloadLink = document.createElement('a');
downloadLink.target = '_blank';
downloadLink.download = 'name_to_give_saved_file.pdf';
// convert downloaded data to a Blob
var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });
// create an object URL from the Blob
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// set object URL as the anchor's href
downloadLink.href = downloadUrl;
// append the anchor to document body
document.body.append(downloadLink);
// fire a click event on the anchor
downloadLink.click();
// cleanup: remove element and revoke object URL
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
}
I changed this:
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
to
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf;base64,'
+ escape(pdfText)
+ '"></embed>';
and it worked for me.
The answer of #alexandre with base64 does the trick.
The explanation why that works for IE is here
https://en.m.wikipedia.org/wiki/Data_URI_scheme
Under header 'format' where it says
Some browsers (Chrome, Opera, Safari, Firefox) accept a non-standard
ordering if both ;base64 and ;charset are supplied, while Internet
Explorer requires that the charset's specification must precede the
base64 token.
I work in PHP and use a function to decode the binary data sent back from the server. I extract the information an input to a simple file.php and view the file through my server and all browser display the pdf artefact.
<?php
$data = 'dfjhdfjhdfjhdfjhjhdfjhdfjhdfjhdfdfjhdf==blah...blah...blah..'
$data = base64_decode($data);
header("Content-type: application/pdf");
header("Content-Length:" . strlen($data ));
header("Content-Disposition: inline; filename=label.pdf");
print $data;
exit(1);
?>
Detect the browser and use Data-URI for Chrome and use PDF.js as below for other browsers.
PDFJS.getDocument(url_of_pdf)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// get a viewport
var scale = 1.5;
var viewport = page.getViewport(scale);
// get or create a canvas
var canvas = ...;
canvas.width = viewport.width;
canvas.height = viewport.height;
// render a page
page.render({
canvasContext: canvas.getContext('2d'),
viewport: viewport
});
})
.catch(function(err) {
// deal with errors here!
});
I saw another question on just this topic recently (streaming pdf into iframe using dataurl only works in chrome).
I've constructed pdfs in the ast and streamed them to the browser. I was creating them first with fdf, then with a pdf class I wrote myself - in each case the pdf was created from data retrieved from a COM object based on a couple of of GET params passed in via the url.
From looking at your data sent recieved in the ajax call, it looks like you're nearly there. I haven't played with the code for a couple of years now and didn't document it as well as I'd have cared to, but - I think all you need to do is set the target of an iframe to be the url you get the pdf from. Though this may not work - the file that oututs the pdf may also have to outut a html response header first.
In a nutshell, this is the output code I used:
//We send to a browser
header('Content-Type: application/pdf');
if(headers_sent())
$this->Error('Some data has already been output, can\'t send PDF file');
header('Content-Length: '.strlen($this->buffer));
header('Content-Disposition: inline; filename="'.$name.'"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
ini_set('zlib.output_compression','0');
echo $this->buffer;
So, without seeing the full response text fro the ajax call I can't really be certain what it is, though I'm inclined to think that the code that outputs the pdf you're requesting may only be doig the equivalent of the last line in the above code. If it's code you have control over, I'd try setting the headers - then this way the browser can just deal with the response text - you don't have to bother doing a thing to it.
I simply constructed a url for the pdf I wanted (a timetable) then created a string that represented the html for an iframe of the desired sie, id etc that used the constructed url as it's src. As soon as I set the inner html of a div to the constructed html string, the browser asked for the pdf and then displayed it when it was received.
function showPdfTt(studentId)
{
var url, tgt;
title = byId("popupTitle");
title.innerHTML = "Timetable for " + studentId;
tgt = byId("popupContent");
url = "pdftimetable.php?";
url += "id="+studentId;
url += "&type=Student";
tgt.innerHTML = "<iframe onload=\"centerElem(byId('box'))\" src='"+url+"' width=\"700px\" height=\"500px\"></iframe>";
}
EDIT: forgot to mention - you can send binary pdf's in this manner. The streams they contain don't need to be ascii85 or hex encoded. I used flate on all the streams in the pdf and it worked fine.
You can use PDF.js to create PDF files from javascript... it's easy to code... hope this solve your doubt!!!
Regards!

HTML anchor tag download attribute not working in Firefox for jpg and png files

In my web application I have supported user to upload any type of document (.png, .jpg, .docx, .xls, ... ) I'm trying to implement download functionality for these documents.
In Google Chrome if you click on Download link Save dialog is shown for all above documents.
In Mozilla Firefox for docx and xls works fine, Save dialog is shown but for .png and .jpg download tag is not working as expected i.e., download dialog or Save dialog does not appear, it directly open that image.
My code:
Download
I have tried almost all solutions mentioned on stackoverflow and suggested by Google. But most of them says that 'check firefox version' and other changes like:
try adding the element to the DOM before triggering the click
Remove filename from download tag it is of boolean type and etc.
I have also tried w3schools lesson on anchor tag and download attribute but nothing seems to be working.
My Mozilla Firefox version is: 38.0.5
P.S.: in chrome as well as in firefox .docs, .xls, .pdf documents work fine, problem is for .png and .jpg in firefox.
Firefox will handle png and jpeg using default handling, which is to inline them in the document. When clicking a link, even if download attribute is defined, seem to make Firefox think it has a new image ignoring the download aspect of it. This may be a temporary bug.
Here is a way, admittedly not super-elegant, to get around this problem forcing the image to be interpreted as an octet-stream.
It does not work inline on Stackoverflow so you have to test it on jsFiddle.
The code does the following:
Scans the document for a-tags.
Those which has data-link set will have a common click-handler attached.
When clicked the link is extracted from the data-link attribute (href is se to #), loaded as an ArrayBuffer via XHR (CORS requirements applies, not a problem in this case), and is converted to an Object-URL with the Blob set to mime-type octet/stream
The Object URL is set as window.location to redirect to this binary data which will make the browser ask user to download the file instead.
var links = document.querySelectorAll("a"), i = 0, lnk;
while(lnk = links[i++]) {
if (lnk.dataset.link.length) lnk.onclick = toBlob;
}
function toBlob(e) {
e.preventDefault();
var lnk = this, xhr = new XMLHttpRequest();
xhr.open("GET", lnk.dataset.link);
xhr.responseType = "blob";
xhr.overrideMimeType("octet/stream");
xhr.onload = function() {
if (xhr.status === 200) {
window.location = (URL || webkitURL).createObjectURL(xhr.response);
}
};
xhr.send();
}
Example tag:
Click to download
The drawback is that you'll loose the extension in the filename.
This is also possible to do using a Data-URL, but a data-url has a 166% overhead compared to using ArrayBuffer and a blob.
I had a similar problem with firefox not handling the download attribute, even for same-domain files.
My target files are actually hosted on AWS, so they are cross-domain. I got around this with a same-domain endpoint that downloads the remote file and pipes it to the client.
const express = require('express')
const {createWriteStream} = require('fs')
const downloadVideo = (url) => { return new Promise((resolve, reject) => {
const filePath = `/tmp/neat.mp4`
const ws = createWriteStream(filePath)
request(url, {}, (error, response, body) => {
if(error) { return reject(error) }
resolve(filePath)
}).pipe(ws)
})}
app.get('/api/download', async (req, res) => {
const videoPath = await downloadVideo(req.query.url)
res.sendFile(videoPath)
})
On the client, I send the file path to the download endpoint to get a blob back, which is then converted to an object url. From there, it's standard download attribute stuff.
async download(remoteFilePath){
const a = document.createElement('a')
const dlURL = `/api/download?url=${encodeURIComponent(remoteFilePath)}`
const blob = await fetch(dlURL).then(res => res.blob())
a.href = URL.createObjectURL(blob)
a.setAttribute('download', 'cool.mp4')
document.body.appendChild(a)
a.click()
a.remove()
}
As you are using HTML5 attribute, each browser handling differently. So use https://github.com/dcneiner/Downloadify for client side forceful download instead of viewing in browser.

reading an object URL produced in a worker with IE

I have a webworker that is producing a CSV for downloading, and to conserve memory I only have it return the URL it produces from teh blob..
My worker code looks something like::
var blob = new Blob([ResultOfSomeWork()],{type:'text/csv'});
URL.createObjectURL(blob);
self.postMessage({url:blob.url});
My goal is to just be able to download it in firefox and chrome this is very easy as I can just set up an invisible <a> and have it be clicked to download it.
For IE10 I want to use msSaveBlob but I need a blob which I don't want to transfer.
How can I download a object dataurl in IE10?
So I found a solution that works. Apparently, I can XHR and read the content back in my main thread.
worker.onmessage = function(event){
var url = event.data.url;
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType='blob';
xhr.onload = function(){
msSaveBlob(xhr.response, fileName +'.csv');
};
xhr.send();
}
While this feels extremely complicated it works well in practice and is really fast.

Chrome extension - saving PDF file

I'm developing a Chrome extension that will save files to the downloads folder (that's not all it's doing, but that's the part I have trouble with). Right now I'm focusing on PDF files. Basically, when a PDF is opened in Chrome, the user can manually save it using Menu -> Save File As ..., I'm just trying to automate this functionality using the extension, but I haven't found a good way to do it.
Let's say I can detect if the current tab has a PDF file in it (based on answers from this question).
The best thing I have figured out so far is to initiate a download:
chrome.downloads.download({
url: tabs[0].url, saveAs: false,
filename: "my file", /* This will be some unique autogenerated identifier */
conflictAction: "overwrite"
});
This works but has 2 drawbacks:
The file has to be re-downloaded, which is a pain if it's large. Besides, the file has been downloaded already so I should be able to use it.
For some reason this doesn't work with files opened locally ("file://..."). It throws a NETWORK_INVALID_REQUEST and doesn't download.
Is there a better way to save the file?
Note, chromium / chrome browsers appear to append embed element to document.body to display .pdf files
a) detecting pdf utilizing window.location.href , document.querySelectorAll("embed")[0].type;
b) utilizing XMLHttpRequest to request existing document, which should return pdf document as blob response, from cache; see console -> Network -> Headers -> Status Code
To allow opening file: protocol at chromium / chrome browsers, try utilizing command line flag --allow-access-from-files; see How do I make the Google Chrome flag “--allow-file-access-from-files” permanent?
At .pdf document , i.e.g; Ecma-262.pdf try
// check if `document` is `pdf`
if (/pdf/i.test(window.location.href.slice(-3))
|| document.querySelectorAll("embed")[0].type === "application/pdf") {
var xhr = new XMLHttpRequest();
// load `document` from `cache`
xhr.open("GET", "", true);
xhr.responseType = "blob";
xhr.onload = function (e) {
if (this.status === 200) {
// `blob` response
console.log(this.response);
var file = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
a.href = file;
a.download = this.response.name
|| document.querySelectorAll("embed")[0].src
.split("/").pop();
document.body.appendChild(a);
a.click();
// remove `a` following `Save As` dialog,
// `window` regains `focus`
window.onfocus = function () {
Array.prototype.forEach.call(document.querySelectorAll("a")
, function (el) {
document.body.removeChild(el)
})
}
};
};
xhr.send();
};
Addressing only the file:// aspect of your problem. Does your extension have permission to access file://. In order to have access your extension both needs to ask for file:/// and user has to manually grant this access from the extensions page. You can check if you have the requisite permission using https://developer.chrome.com/extensions/extension#method-isAllowedFileSchemeAccess.
See Adding file://. permission to chrome extension for more information about accessing file:// urls. You may also find How can I enable my chrome extension in incognito mode? helpful.
For a related discussion (although not specific for your use case since you already have a PDF file), also see https://code.google.com/p/chromium/issues/detail?id=169337.

'Access is denied' exception trying to download binary file stream

I am receiving an XmlHttpRequest's response in my code, which is a binary stream of a file. The response has the correct size and mimetype. I would like to download this file to disk, by issuing the typical browser prompt for saving files. I have the following script in place which works perfectly fine in Chrome. It fails for IE though.
var url = /my_rest_url/;
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.onreadystatechange = function (e) {
if (xhr.readyState == xhr.DONE) {
var blob = xhr.response;
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
}
}
xhr.send();
The failure happens on the window.open() call, where IE fails with an exception that says "access is denied". I have researched for hours on end without finding a solution which works for me. I have seen some recommendations to use a 'hidden iframe' to start the download, but it hasn't worked out for me. Has anyone ever made something like this work in IE? Would be glad to get some ideas.
ps/ I know blob and createObjectURL are relatively new. And even though they are supported by IE10 and above, I would really feel better to get rid of these and achieve this an 'old-school' way, if there is any.

Categories