Help me I am in desperate need for help. Now to the real issue.
I have been trying to get my pdf to open up in new tab in Safari but till now I've only faced disappointment. I am using jsPDF for generating my PDFs and earlier I used it's
doc.save(pdfName+".pdf")
method to do the same, as in the latest safari version "9.1.1(10601.6.17)" it broke. I can still generate the pdf using jsPDF's inbuilt option:
pdf.output('dataurl');
but, it tends to open the PDF in the same tab, and this behaviour is highly undesirable. I tried various solutions available on the net to fix the issue but none works. Some of the solutions that I tried are :
Using window.open(), doesn't work.
Using location.href, doesn't work.
Using doc.output('save', 'filename.pdf'), doc.output('datauri'), doc.output('dataurlnewwindow'), nothing works.
Also tried fake clicking by creating an anchor tag and setting it's target as '_blank' and then using eventDispatcher to stimulate a mouse click but it also didn't work. Something like this :
var a = window.document.createElement("a");
a.target = '_blank';
a.href = 'http://www.google.com';
var e = window.document.createEvent("MouseEvents");
e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
Any suggestion on what I should do ? I want my pdf to open up in a new tab and not in the same one as that of my application.
P.S : Each of the solutions that I tried for Safari above work flawlessly in Chrome and Mozilla Firefox, I just can't figure out what's the deal with Safari. Any help would be appreciated.
Tried many solution mentioned in stackoverflow but nothing worked for me, Later checked the FileSave.js implementation and created a working solution for this issue.
Note that I have successfully tested this solution in iOS Safari.
In function downloadPdfMobile the if part is for Web safari else part is for mobile safari.
Normal Android chrome browser this will not work, you need to follow the traditional way.
First you need to get the base64 string from the jsPDF
const doc = new jsPDF.jsPDF({ unit: 'pt', putOnlyUsedFonts: true});
const pdfElement = document.getElementById('id');
var pdfName = 'Test.pdf';
doc.html(pdfElement, {
callback: (pdf) => {
var base = pdf.output('datauristring').split(',')[1];
downloadPdfMobile(pdfName, base);
},
margin: 20,
});
This is how the downloadPdfMobile function looks like, First you need to convert the base64 t Blob.
downloadPdfMobile: function (FileName, content) {
var _global =
typeof window === "object" && window.window === window
? window
: typeof self === "object" && self.self === self
? self
: typeof global === "object" && global.global === global
? global
: this;
var isMacOSWebView =
_global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent);
var blob = this.base64toBlob(content);
var popup = open("", "_blank");
if (popup) {
popup.document.title = popup.document.body.innerText = "downloading...";
}
var force = blob.type === "application/octet-stream";
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== "undefined") {
var reader = new FileReader();
reader.onloadend = function () {
var url = reader.result;
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, "data:attachment/file;");
if (popup) popup.location.href = url;
else location = url;
popup = null;
};
reader.readAsDataURL(blob);
} else {
var URL = _global.URL || _global.webkitURL;
var url = URL.createObjectURL(blob);
if (popup) popup.location = url;
else location.href = url;
popup = null;
setTimeout(function () {
URL.revokeObjectURL(url);
}, 4e4);
}
}
base64toBlob: function (base64str) {
var binary = atob(base64str.replace(/\s/g, ""));
var len = binary.length;
var buffer = new ArrayBuffer(len);
var view = new Uint8Array(buffer);
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
return new Blob([view], {
type: "application/pdf",
});
},
If you can get your PDF as base64, this is how you can do with it (works on both Safari and Chrome)
let base64PDF = "JVBERi0xLjQKMSAwIG9iago8PAovVGl0bGUgKP....."// your base64 pdf data
let pdfWindow = window.open("", "_blank");
pdfWindow.document.write(
"<title>Healthcare</title><iframe width='100%' style='margin: -8px;border: none;' height='100%' src='data:application/pdf;base64, " +
encodeURI(base64PDF) +
"'></iframe>"
);
You could try the blob output. Then generate a Data URI to this, and open it in a new window:
var blob = pdf.output("blob");
window.open(URL.createObjectURL(blob));
Related
I am currently working on a web page, which gives the possibility of downloading reports in pdf format. The report is generated on the server and the request is made through "Json".
Once the response is obtained, the "pdf" document is displayed in a new browser tab.
The problem I have is that the file name is kept in a base64 format (very very long).
Is there a way to change this file name?
My code that currently works:
$(document).on('click', '.pdfInforme', function (e) {
e.preventDefault();
var data = $(this).attr("ord");
var d = data.split("*");
var ord = d[0];
var pac = d[1];
$('#loading-indicator').show();
var $modal = $(this);
$.ajax({
type: "POST",
url: 'Download.aspx/ServerRequest',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({ 'ord': ord }),
success: function (result) {
//pdf
//Is Firefox?
var nav = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
var byteCharacters = atob(result.d);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var file = new Blob([byteArray], { type: 'application/pdf;base64' });
const dataURI = result.d;
const blob = file;
const url = URL.createObjectURL(blob);
const blobAnchor = document.createElement('a');
const dataURIAnchor = document.createElement('a');
blobAnchor.download = 'Informe_' + pac + '_' + ord;
dataURIAnchor.download = 'Informe_' + pac + '_' + ord;
blobAnchor.className = 'hidden';
blobAnchor.href = url;
dataURIAnchor.href = dataURI;
document.body.appendChild(blobAnchor);
blobAnchor.onclick = function () {
requestAnimationFrame(function () {
URL.revokeObjectURL(url);
setTimeout(() => blobAnchor.remove(), 300);
});
};
if(nav)
blobAnchor.click();
else
window.open(blobAnchor, '_blank');
$("#downloadzip").remove();
$('#loading-indicator').hide();
},
error: function (req, status, error) {
alert(error);
}
});
});
I have tried: (It only works to change the title of the new tab)
document.title="New File Name";
I found a solution for chrome and edge (very close to solving it):
Download PDF not working with Firefox using Angular 2 and Node
Using this solution i can open a file in a new tab with a short name (eg "0a06ff23-cea1-417b-87ac-ec591315786e") using "blob". But, I still can't modify file name to another default name.
On the other hand, this method does not work in Firefox, where new tab does not even open (it does in Chrome and Edge). In any case, it would be at least a partial solution.
Upgrade
I noticed that Firefox was downloading the file instead of opening it in a new tab. Therefore, I check if it is a firefox browser, and perform different actions for each case. In case of Firefox, the file is downloaded with the default name I chose, and when the browser is Chrome or Edge, the file opens in a new tab with a name similar to "0a06ff23-cea1-417b-87ac- ec591315786e". It is tested on Android and Apple, and in both cases the possibility of downloading the file is given and then it opens automatically.
I have a blob created with a base64, and I need to make this data downloadable as a pdf.
I created this snippet:
var blob = new Blob([byte]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.target = '_blank';
var fileName = name + '.pdf';
link.download = fileName;
link.click();
It works on all the browsers, except safari mobile on iOS.
The file gets actually downloaded, but its name is "unknown", then it can't be open since the extension gets lost.
The problem is that the download attribute lacks support on this browser and IE.
There are a lot of workarounds for IE, but I didn't find any for Safari/iOS.
Do you know how can I download a blob got from a base64 (no XHR involved) in this browser?
Thank you
I need to make this data downloadable as a pdf (...) in safari iOS
SHORT ANSWER: you can't. Due this bug is impossible to download the file on safari iOS
The alternative is to open the file on the browser with the proper mime type, so it can show its content (and the user can then manually download it if needed).
Make sure to pass mime type when creating the Blob. reference
var blob = new Blob([byte], {type: 'application/pdf'});
Lastly, I'd strongly suggest you to use FileSaver.js which that can handle most of the corner cases/multiple browser support for save (or in this case, open) a file in javascript.
As per the below link:-
https://caniuse.com/#feat=download
Safari 13 Beta 3 is released so you can check on the same, whether its working or not?
You can download a blob got from a base64 by using a atob function.
The atob function will decode a base64-encoded string into a new string with a character for each byte of the binary data.
You can save blob locally via FileSaver.js .
You can also check here that would be helpful:-
How to open Blob URL on Chrome iOS
This is something I have tried in my project and it is working for me.
import "./styles.css";
var pdfData =
"JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog" +
"IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv" +
"TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K" +
"Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg" +
"L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+" +
"PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u" +
"dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq" +
"Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU" +
"CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu" +
"ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g" +
"CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw" +
"MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v" +
"dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G";
let download = () => {
if (pdfData) {
var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
var isChrome =
navigator.userAgent.toLowerCase().indexOf("CriOS") > -1 ||
navigator.vendor.toLowerCase().indexOf("google") > -1;
var iOSVersion = [];
if (iOS) {
iOSVersion = navigator.userAgent
.match(/OS [\d_]+/i)[0]
.substr(3)
.split("_")
.map((n) => parseInt(n));
}
var attachmentData = pdfData;
var attachmentName = "Test.pdf";
var contentType = "application/pdf";
var binary = atob(attachmentData.replace(/\s/g, ""));
var len = binary.length;
var buffer = new ArrayBuffer(len);
var view = new Uint8Array(buffer);
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
var linkElement = document.createElement("a");
try {
var hrefUrl = "";
var blob = "";
if (iOS && !isChrome && iOSVersion[0] <= 12) {
blob = "data:application/pdf;base64," + pdfData;
hrefUrl = blob;
} else {
if (iOS && !isChrome) {
contentType = "application/octet-stream";
}
blob = new Blob([view], { type: contentType });
hrefUrl = window.URL.createObjectURL(blob);
}
linkElement.setAttribute("href", hrefUrl);
linkElement.setAttribute("target", "_blank");
if ((iOS && (iOSVersion[0] > 12 || isChrome)) || !iOS) {
linkElement.setAttribute("download", attachmentName);
}
var clickEvent = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: false
});
linkElement.dispatchEvent(clickEvent);
} catch (ex) {}
}
};
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={download}>Download</button>
</div>
);
}
The "target" attribute in Safari seems to override the "download" attribute. Currently, as to my knowledge, there is no way to solve this. So I think you have to wait for the next Safari version (13) which will be out in a few months.
Before somebody says, "duplicate", I just want to make sure, that folks know, that I have already reviewed these questions:
1) Uses angular and php, not sure what is happening here (I don't know PHP): Download zip file and trigger "save file" dialog from angular method
2) Can't get this answer to do anything: how to download a zip file using angular
3) This person can already download, which is past the point I'm trying to figure out:
Download external zip file from angular triggered on a button action
4) No answer for this one:
download .zip file from server in nodejs
5) I don't know what language this even is:
https://stackoverflow.com/questions/35596764/zip-file-download-using-angularjs-directive
Given those questions, if this is still a duplicate, I apologize. Here is, yet, another version of this question.
My angular 1.5.X client gives me a list of titles, of which each have an associated file. My Node 4.X/Express 4.X server takes that list, gets the file locations, creates a zip file, using express-zip from npm, and then streams that file back in the response. I then want my client to initiate the browser's "download a file" option.
Here's my client code (Angular 1.5.X):
function bulkdownload(titles){
titles = titles || [];
if ( titles.length > 0 ) {
$http.get('/query/bulkdownload',{
params:{titles:titles},
responseType:'arraybuffer'
})
.then(successCb,errorCb)
.catch(exceptionCb);
}
function successCb(response){
// This is the part I believe I cannot get to work, my code snippet is below
};
function errorCb(error){
alert('Error: ' + JSON.stringify(error));
};
function exceptionCb(ex){
alert('Exception: ' + JSON.stringify(ex));
};
};
Node (4.X) code with express-zip, https://www.npmjs.com/package/express-zip:
router.get('/bulkdownload',function(req,resp){
var titles = req.query.titles || [];
if ( titles.length > 0 ){
utils.getFileLocations(titles).
then(function(files){
let filename = 'zipfile.zip';
// .zip sets Content-Type and Content-disposition
resp.zip(files,filename,console.log);
},
_errorCb)
}
});
Here's my successCb in my client code (Angular 1.5.X):
function successCb(response){
var URL = $window.URL || $window.webkitURL || $window.mozURL || $window.msURL;
if ( URL ) {
var blob = new Blob([response.data],{type:'application/zip'});
var url = URL.createObjectURL(blob);
$window.open(url);
}
};
The "blob" part seems to work fine. Checking it in IE's debugger, it does look like a file stream of octet information. Now, I believe I need to get that blob into the some HTML5 directive, to initiate the "Save File As" from the browser. Maybe? Maybe not?
Since 90%+ of our users are using IE11, I test all of my angular in PhantomJS (Karma) and IE. When I run the code, I get the old "Access is denied" error in an alert window:
Exception: {"description":"Access is denied...<stack trace>}
Suggestions, clarifications, answers, etc. are welcome!
Use this one:
var url="YOUR ZIP URL HERE";
window.open(url, '_blank');
var zip_file_path = "" //put inside "" your path with file.zip
var zip_file_name = "" //put inside "" file name or something
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = zip_file_path;
a.download = zip_file_name;
a.click();
document.body.removeChild(a);
As indicated in this answer, I have used the below Javascript function and now I am able to download the byte[] array content successfully.
Function to convert byte array stream (type of string) to blob object:
var b64toBlob = function(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
};
An this is how I call this function and save the blob object with FileSaver.js (getting data via Angular.js $http.get):
$http.get("your/api/uri").success(function(data, status, headers, config) {
//Here, data is type of string
var blob = b64toBlob(data, 'application/zip');
var fileName = "download.zip";
saveAs(blob, fileName);
});
Note: I am sending the byte[] array (Java-Server-Side) like this:
byte[] myByteArray = /*generate your zip file and convert into byte array*/ new byte[]();
return new ResponseEntity<byte[]>(myByteArray , headers, HttpStatus.OK);
I updated my bulkdownload method to use $window.open(...) instead of $http.get(...):
function bulkdownload(titles){
titles = titles || [];
if ( titles.length > 0 ) {
var url = '/query/bulkdownload?';
var len = titles.length;
for ( var ii = 0; ii < len; ii++ ) {
url = url + 'titles=' + titles[ii];
if ( ii < len-1 ) {
url = url + '&';
}
}
$window.open(url);
}
};
I have only tested this in IE11.
I have a mp3 link like this :
http://example.com/932937293723.mp3
but i want to rename it when user downloads the file to be like this
http://example.com/Artist - Title.mp3
My code :
DOWNLOAD
The mp3 file stored in remote server. And i'm not the owner of that server.
HTML download attribute seem not good solution. because it's not cross-browser. Any cross-browser solution to solve this ? Javascript maybe :D
If you insist on working from the front end, try working with the following code. The getblob method is depreciated, but you need to update that side. Let me know.
function getBinary(file){
var xhr = new XMLHttpRequest();
xhr.open("GET", file, false);
xhr.overrideMimeType("text/plain; charset=x-user-defined");
xhr.send(null);
return xhr.responseText;
}
function sendBinary(data, url){
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
if (typeof XMLHttpRequest.prototype.sendAsBinary == "function") { // Firefox 3 & 4
var tmp = '';
for (var i = 0; i < data.length; i++) tmp += String.fromCharCode(data.charCodeAt(i) & 0xff);
data = tmp;
}
else { // Chrome 9
// http://javascript0.org/wiki/Portable_sendAsBinary
XMLHttpRequest.prototype.sendAsBinary = function(text){
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);
var bb = new BlobBuilder(); // doesn't exist in Firefox 4
bb.append(data);
var blob = bb.getBlob();
this.send(blob);
}
}
xhr.sendAsBinary(data);
}
var data = getBinary("My music.mp3");
sendBinary(data,'http://www.tonycuffe.com/mp3/tailtoddle_lo.mp3');
In your back end code, you can fetch the file to your server, store it to a variable, rename it from there, define the corresponding headers, and return it. this could happen as an ajax call initiated on the javascript click.
Post further details about your backed and i can help you more.
You can use something like below (ASP.NET)
In ASPX
Download
In ASP.NET
Response.ContentType = "audio/mpeg3";
Response.AddHeader("content-disposition", "attachment;filename=New_file_name.mp3");
Server.Transfer(decoded_URL_of_MP3_file);
Look here for other MIME types
Update#1 - Using Javascript alone, you can try something like this, though I've not tested in different browsers
function Download(url, fancyFileName)
{
var file = document.createElement('a');
file.href = url;
file.target = '_blank';
file.download = fancyFileName;
var event = document.createEvent('Event');
event.initEvent('click', true, true);
file.dispatchEvent(event);
window.URL.revokeObjectURL(file.href);
}
Download('http://server.com/file.mp3','Artist_file.mp3');
Trying to initiate a browser download in Javascript, but the data I want to be downloaded is in a string, not a file. I know if it were a file, the following would do it:
window.location.href = '/filepath/file.csv';
How can I get this same effect, only with a string (with csv data), not a file that already exists on the server?
using my handy downloader:
<script src="http://danml.com/js/download.js"></script>
<script>download("hello world", "hello.txt", "text/plain")</script>
you can do it without a library as well, though my "lib" isn't very big and supports older FF+CH and IE10:
<a id=dl download="file.txt">Download</a>
<script>
content=prompt("enter contents");
dl.href="data:text/plain,"+encodeURIComponent(content);
dl.click();
</script>
EDIT: the linked script now supports window.URL.createObjectURL() for downloading files that were too big using dataURLs. I don't know the new limit, but 10mb works just file, whereas ~2mb is a limit for many dataURL ( window.open/A[download] - based ) solutions3
Below is a function I have writen in the past to handle such behavior (it may require some tweaking):
var downloadFile = function (filename, dataValue) {
window.URL = window.webkitURL || window.URL;
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder;
var prevLink = output.querySelector('a');
if (prevLink) {
window.URL.revokeObjectURL(prevLink.href);
output.innerHTML = '';
}
var a = document.createElement('a');
a.download = '" + filename + #".csv';
if (BlobBuilder == undefined) {
var bb = new Blob([dataValue], { 'type': MIME_TYPE });
a.href = window.URL.createObjectURL(bb);
}
else {
var bb = new BlobBuilder();
bb.append(dataValue);
a.href = window.URL.createObjectURL(bb.getBlob(MIME_TYPE));
}
a.textContent = 'Download ready';
a.dataset.downloadurl = [MIME_TYPE, a.download, a.href].join(':');
a.draggable = true; // Don't really need, but good practice.
a.classList.add('dragout');
output.appendChild(a);
a.onclick = function (e) {
if ('disabled' in this.dataset) {
return false;
}
};
};