Creating download prompt using purely javascript - javascript

I have some text data (say var a = 'Hello World From Javascript';)in javascript variable in current window. I want to do the following
through javascript-
1. open a new window and write the text data to the window.
2. set the content type to text/plain.
3. set the content-disposition to attachment, so that download prompt comes.
4. user downloads the text data as a text file and saves it to his local disk.
is this all possible through javascript?
I know we can make ajax calls to server or redirect but in this case instead of following above steps. But in this case, these workarounds are not adaptable.

you can do that using JS & HTML5 features. Please find below a sample code.
var fileParts = ['Hello World From Javascript'];
// Create a blob object.
var bb = new Blob(fileParts,{type : 'text/plain'});
// Create a blob url for this.
var dnlnk = window.URL.createObjectURL(bb);
var currentLnk = $('#blobFl').attr('href');
// blobFl is the id of the anchor tag through which the download will be triggered.
$('#blobFl').attr('href',dnlnk);
$('#blobFl').attr('download','helloworld.txt');
// For some reason trigger from jquery dint work for me.
document.getElementById('blobFl').click();

Triggering a file download without any server request
Unfortunately this is not something you can do with normal browser capabilities. Something like flash or a browser-specific plugin will get you what you need, but security limitations within javascript will not let you download arbitrary data created within the browser.
Also the 'data' url is not supported across all browser/version combinations. I am not sure if your users are constrained on what browser they are using or not but that may limit what you can do with that solution.
Source: Triggering a file download without any server request

If you already have the file on the server (I make an ajax call to generate and save a PDF on the server) - you can do this
window.location.replace(fileUrl);

No, Content-Disposition is a response header, it has to come from the server. I think you could do it with Flash but I wouldn't recommend it.

Here's a clean, pure js version of #Rajagopalan Srinivasan's answer:
var fileParts = ["Hello World From Javascript"];
// The anchor tag to use.
const blobLink = document.getElementById("blobLink");
// Create a blob object.
var blob = new Blob(fileParts, { type: "text/plain" });
// Create a blob url for this.
var blobUrl = window.URL.createObjectURL(blob);
blobLink.setAttribute("href", blobUrl);
blobLink.setAttribute("download", "helloworld.txt");
blobLink.click();
<a id="blobLink">Download</a>

Related

How to take a snapshot of webpage from the url?

I want to take a snapshot of the webpage from the url. The url is an html web page which is dynamic. Basically we needed an img of that webpage.
I thought to convert the html page to image in c# but din't work.
I first read the html using streamreader and using NReco.ImageGenerator tried to convert into bytes and finally image. This isnt working.
Finally I am trying to convert html to canvas using javascript from inside the html web page.
function report() {
let region = document.querySelector("body");
html2canvas(
$('body'),
{allowTaint: true, logging: true,'onrendered': function (canvas)
{}}).then( //getting problem here at then
function (canvas) {
let jpgUrl = canvas.toDataURL();
console.log(jpgUrl);
var text = "bottom-right Brochure1";
var imageName = text + '.jpg';
download(jpgUrl,imageName, "image/png");
}
Code explained - It will take a snpashot of the body element in the html page using js. Take the url and create the canvas from the url. and automatically download. But i face a issue ------- " html2canvas(...),then is not a function" .. I dont know why its happening. Please help.
Because of Cross-Origin Resource Sharing (CORS) restrictions in all modern browsers, this can't be done purely on the clientside. You need something on the server-side to accomplish this. To do it in javascript on the server use NodeJS there are several npm packages that can help like: node-server-screenshot, PhantomJS etc

Blob name issue with new tab in chrome and firefox [duplicate]

In my Vue app I receive a PDF as a blob, and want to display it using the browser's PDF viewer.
I convert it to a file, and generate an object url:
const blobFile = new File([blob], `my-file-name.pdf`, { type: 'application/pdf' })
this.invoiceUrl = window.URL.createObjectURL(blobFile)
Then I display it by setting that URL as the data attribute of an object element.
<object
:data="invoiceUrl"
type="application/pdf"
width="100%"
style="height: 100vh;">
</object>
The browser then displays the PDF using the PDF viewer. However, in Chrome, the file name that I provide (here, my-file-name.pdf) is not used: I see a hash in the title bar of the PDF viewer, and when I download the file using either 'right click -> Save as...' or the viewer's controls, it saves the file with the blob's hash (cda675a6-10af-42f3-aa68-8795aa8c377d or similar).
The viewer and file name work as I'd hoped in Firefox; it's only Chrome in which the file name is not used.
Is there any way, using native Javascript (including ES6, but no 3rd party dependencies other than Vue), to set the filename for a blob / object element in Chrome?
[edit] If it helps, the response has the following relevant headers:
Content-Type: application/pdf; charset=utf-8
Transfer-Encoding: chunked
Content-Disposition: attachment; filename*=utf-8''Invoice%2016246.pdf;
Content-Description: File Transfer
Content-Encoding: gzip
Chrome's extension seems to rely on the resource name set in the URI, i.e the file.ext in protocol://domain/path/file.ext.
So if your original URI contains that filename, the easiest might be to simply make your <object>'s data to the URI you fetched the pdf from directly, instead of going the Blob's way.
Now, there are cases it can't be done, and for these, there is a convoluted way, which might not work in future versions of Chrome, and probably not in other browsers, requiring to set up a Service Worker.
As we first said, Chrome parses the URI in search of a filename, so what we have to do, is to have an URI, with this filename, pointing to our blob:// URI.
To do so, we can use the Cache API, store our File as Request in there using our URL, and then retrieve that File from the Cache in the ServiceWorker.
Or in code,
From the main page
// register our ServiceWorker
navigator.serviceWorker.register('/sw.js')
.then(...
...
async function displayRenamedPDF(file, filename) {
// we use an hard-coded fake path
// to not interfere with legit requests
const reg_path = "/name-forcer/";
const url = reg_path + filename;
// store our File in the Cache
const store = await caches.open( "name-forcer" );
await store.put( url, new Response( file ) );
const frame = document.createElement( "iframe" );
frame.width = 400
frame.height = 500;
document.body.append( frame );
// makes the request to the File we just cached
frame.src = url;
// not needed anymore
frame.onload = (evt) => store.delete( url );
}
In the ServiceWorker sw.js
self.addEventListener('fetch', (event) => {
event.respondWith( (async () => {
const store = await caches.open("name-forcer");
const req = event.request;
const cached = await store.match( req );
return cached || fetch( req );
})() );
});
Live example (source)
Edit: This actually doesn't work in Chrome...
While it does set correctly the filename in the dialog, they seem to be unable to retrieve the file when saving it to the disk...
They don't seem to perform a Network request (and thus our SW isn't catching anything), and I don't really know where to look now.
Still this may be a good ground for future work on this.
And an other solution, I didn't took the time to check by myself, would be to run your own pdf viewer.
Mozilla has made its js based plugin pdf.js available, so from there we should be able to set the filename (even though once again I didn't dug there yet).
And as final note, Firefox is able to use the name property of a File Object a blobURI points to.
So even though it's not what OP asked for, in FF all it requires is
const file = new File([blob], filename);
const url = URL.createObjectURL(file);
object.data = url;
In Chrome, the filename is derived from the URL, so as long as you are using a blob URL, the short answer is "No, you cannot set the filename of a PDF object displayed in Chrome." You have no control over the UUID assigned to the blob URL and no way to override that as the name of the page using the object element. It is possible that inside the PDF a title is specified, and that will appear in the PDF viewer as the document name, but you still get the hash name when downloading.
This appears to be a security precaution, but I cannot say for sure.
Of course, if you have control over the URL, you can easily set the PDF filename by changing the URL.
I believe Kaiido's answer expresses, briefly, the best solution here:
"if your original URI contains that filename, the easiest might be to simply make your object's data to the URI you fetched the pdf from directly"
Especially for those coming from this similar question, it would have helped me to have more description of a specific implementation (working for pdfs) that allows the best user experience, especially when serving files that are generated on the fly.
The trick here is using a two-step process that perfectly mimics a normal link or button click. The client must (step 1) request the file be generated and stored server-side long enough for the client to (step 2) request the file itself. This requires you have some mechanism supporting unique identification of the file on disk or in a cache.
Without this process, the user will just see a blank tab while file-generation is in-progress and if it fails, then they'll just get the browser's ERR_TIMED_OUT page. Even if it succeeds, they'll have a hash in the title bar of the PDF viewer tab, and the save dialog will have the same hash as the suggested filename.
Here's the play-by-play to do better:
You can use an anchor tag or a button for the "download" or "view in browser" elements
Step 1 of 2 on the client: that element's click event can make a request for the file to be generated only (not transmitted).
Step 1 of 2 on the server: generate the file and hold on to it. Return only the filename to the client.
Step 2 of 2 on the client:
If viewing the file in the browser, use the filename returned from the generate request to then invoke window.open('view_file/<filename>?fileId=1'). That is the only way to indirectly control the name of the file as shown in the tab title and in any subsequent save dialog.
If downloading, just invoke window.open('download_file?fileId=1').
Step 2 of 2 on the server:
view_file(filename, fileId) handler just needs to serve the file using the fileId and ignore the filename parameter. In .NET, you can use a FileContentResult like File(bytes, contentType);
download_file(fileId) must set the filename via the Content-Disposition header as shown here. In .NET, that's return File(bytes, contentType, desiredFilename);
client-side download example:
download_link_clicked() {
// show spinner
ajaxGet(generate_file_url,
{},
(response) => {
// success!
// the server-side is responsible for setting the name
// of the file when it is being downloaded
window.open('download_file?fileId=1', "_blank");
// hide spinner
},
() => { // failure
// hide spinner
// proglem, notify pattern
},
null
);
client-side view example:
view_link_clicked() {
// show spinner
ajaxGet(generate_file_url,
{},
(response) => {
// success!
let filename = response.filename;
// simplest, reliable method I know of for controlling
// the filename of the PDF when viewed in the browser
window.open('view_file/'+filename+'?fileId=1')
// hide spinner
},
() => { // failure
// hide spinner
// proglem, notify pattern
},
null
);
I'm using the library pdf-lib, you can click here to learn more about the library.
I solved part of this problem by using api Document.setTitle("Some title text you want"),
Browser displayed my title correctly, but when click the download button, file name is still previous UUID. Perhaps there is other api in the library that allows you to modify download file name.

JavaScript - load PDF file from URL (cross domain) into variable

I would like to load PDF file from URL into JavaScript variable (this file is on another domain) and then print the base64 encoded string of that file.
This script allows me to browse file on my computer and then it prints base64 string into browser console:
<input id="inputFile" type="file" onchange="convertToBase64();" />
<script type="text/javascript">
function convertToBase64() {
//Read File
var selectedFile = document.getElementById("inputFile").files;
//Check File is not Empty
if (selectedFile.length > 0) {
// Select the very first file from list
var fileToLoad = selectedFile[0];
// FileReader function for read the file.
var fileReader = new FileReader();
var base64;
// Onload of file read the file content
fileReader.onload = function(fileLoadedEvent) {
base64 = fileLoadedEvent.target.result;
// Print data in console
console.log(base64);
};
// Convert data to base64
fileReader.readAsDataURL(fileToLoad);
}
}
</script>
I would like to completely remove the input button from this script and pass my file to variable var selectedFile from URL (for example: http://www.example.com/docs/document.pdf).
I'd need a help how to realize this, because I am not sure if XMLHttpRequest() works cross domain and scripts I've found with Ajax/jQuery method operated mainly with JSON file, which is something different that I need.
Thank you very much for help.
You cannot do this in normal browser-based JavaScript* if the other side (http://www.example.com in your case) doesn't allow cross-origin requests from your origin.
If the other side does let you do this, then yes, you'd use XMLHttpRequest (or jQuery's wrappers for it, such as ajax or get) to request the data and transform/display it as you see fit.
A fairly typical way to work around that if the other side doesn't is to use your own server in-between: Make the request to your server, have it make the request to the other side (server-side code doesn't have the Same Origin Policy blocks that browsers impose), and then have your server respond to your request with the data from the other server.
* "normal browser-based JavaScript" - e.g., without starting the browser with special flags that disable security, or getting people to install an extension, etc.

save a field value to a file on desktop

I am developing a custom application in "ServiceNow" which requires Javascript and HTML coding. So, I have a field say, "description" on my form. How may I save this field's value to a word document on the desktop?
While JavaScript cannot create a file for you to download by itself, ServiceNow does have a way for you to create one. Creating a Word document is impossible without the use of a MID server and some custom Java code, but if any file type will do you can create an Excel file using an export URL. To test this out, I made a UI Action in a developer instance running Helsinki on the Problem table. I made a list view that contains only the field that I wanted to save, and then used the following code in the UI action:
function startDownload() {
window.open("https://dev13274.service-now.com/problem_list.do?EXCEL&sysparm_query=sys_id%3D" +
g_form.getUniqueValue() + "&sysparm_first_row=1&sysparm_view=download");
}
When the UI action is used, it opens a new tab that will close almost immediately and prompt the user to save or open an Excel file that contains the contents of that single field.
If you want to know more about the different ways you can export data from ServiceNow, check their wiki-page on the subject.
You can use the HTML5 FileSystem API to achieve that
window.requestFileSystem(window.PERSISTENT, 1024*1024, function (fs) {
fs.root.getFile('file.txt', {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
var blob = new Blob([description.value], {type: 'text/plain'});
fileWriter.write(blob);
});
});
});
FYI, chrome supports webkitRequestFileSystem.
Alternatively, use a Blob and generate download link
var text = document.getElementById("description").value;
var blob = new Blob([text], {type:'text/plain'});
var fileName = "test.txt";
var downloadLink = document.createElement("a");
downloadLink.download = fileName;
downloadLink.href = window.webkitURL.createObjectURL(textFile);
downloadLink.click();
Javascript protects clients against malicious servers who would want to read files on their computer. For that reason, you cannot read or write a file to the client's computer with javascript UNLESS you use some kind of file upload control that implicitely asks for the user's permission.

How to save a png from javascript variable

I have an image encoded in base64 in a javascript variable : data:image/png;base64, base64 data
[EDIT]
I need to save that file to disk without asking to the visitor to do a right click
[/EDIT]
Is it possible ? How ?
Thanks in advance
Best regards
I know this question is 2 years old, but hopefully people will see this update.
You can prompt the user to save an image in a base64 string (and also set the filename), without asking the user to do a right click
var download = document.createElement('a');
download.href = dataURI;
download.download = filename;
download.click();
Example:
var download = document.createElement('a');
download.href = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
download.download = 'reddot.png';
download.click();
In order to trigger a click event using Firefox, you need to do what it is explained in this SO answer. Basically:
function fireEvent(obj,evt){
var fireOnThis = obj;
if(document.createEvent ) {
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( evt, true, false );
fireOnThis.dispatchEvent( evObj );
} else if( document.createEventObject ) {
var evObj = document.createEventObject();
fireOnThis.fireEvent( 'on' + evt, evObj );
}
}
fireEvent(download, 'click')
As of 20/03/2013, the only browser that fully supports the download attribute is Chrome. Check the compatibility table here
... without asking to the visitor anyhing ... Is it possible?
No, that would have been a security hole. If it was possible, one would be able to write malware to the enduser's disk unaskingly. Your best bet may be a (signed) Java Applet. True, it costs a bit of $$$ to get it signed (so that it doesn't pop security warnings), but it is able to write data to enduser's disk without its permission.
I am surprised nobody here mentioned using HTML5 blobs together with a couple of nice libraries.
You first need https://github.com/eligrey/FileSaver.js/ and https://github.com/blueimp/JavaScript-Canvas-to-Blob.
Then you can load the image into a canvas
base_image = new Image();
base_image.src ='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
the canvas into a blob
var canvas = document.getElementById('YourCanvas');
context = canvas.getContext('2d');
// Draw image within
context.drawImage(base_image, 0,0);
and finally save it
x_canvas.toBlob(function(blob) {
saveAs(blob, "screenshot.png");
}, "image/png");
FF is not fully supported but at least you get a separate page with the image.
Check this out: http://jsfiddle.net/khhmm/9/
EDIT: this is not compatible with Safari / Mac.
As other answers already stated, you cannot do it only with javascript. If you want, you can send the data (using normal HTTP POST) to a PHP script, call header('Content-type: image/png') and output the decoded image data to the page using echo base64_decode($base64data).
This will work just as if user clicked on an image and open it or prompt him to save the file to disk (the normal browser's save file dialog).
It's not possible.
If it was, browsers would be massively insecure, being able to write random data to your hard disk without user interaction.
with javascript, you can't. the only real possibility i can think of will be a java-applet, but maybe (i don't know how long that image should be saved) you could simply add an img-tag with you png and force caching (but if the user deletes his cache, the image will be gone).
I think it's possible with JavaScript if you use ActiveX.
Another possibility is to make the server spit out that file with a different mime type so the browser asks the user to save it.
I think you can do it something(maybe not only with javascript...xul programming needed). There are Firefox addons that save images to a folder(check Firefox addons site)
You can make this file as blob on the server and use setTimeout function in order to fire the download.
The accepted solution seems to have a limitation for large data. If you're running into this (instead of the downloaded file's name, I see "download" and "Failed - Network error" in Chrome), here's what I did in order to download a 2mb file:
const blob = await (await fetch(document.getElementById('canvasID').toDataURL())).blob();
const file = new File([blob], {type:"image/png", lastModified: new Date()});
var a = document.createElement('a');
a.href = window.URL.createObjectURL(file);
a.download = 'image.png';
a.click();

Categories

Resources