Generating Files with JavaScript

Demo Download

When building a web application, you oftentimes need to give users the ability to download a piece of data as a file. It could be a backup of configuration settings, reports, or other piece of information that is generated dynamically.

The usual solution to this problem would be to have a dedicated export script that selects from a database and builds the file you need. However, as we will be proving in this short tutorial, there is another way.

We will make a jQuery plugin which, combined with a simple php script, can generate every kind of textual file, and make it available for download. You will initiate the download from your JavaScript front end by only providing the file contents, and leave the rest to the plugin.

The HTML

We shall start by laying down a simple HTML page with a textarea and a download button, so we can demonstrate the plugin at work.

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />

        <title>Generating files with JS &amp; PHP | Tutorialzine Demo</title>

        <!-- Our CSS stylesheet file -->
        <link rel="stylesheet" href="assets/css/styles.css" />

        <!--[if lt IE 9]>
          <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
    </head>

    <body>

        <header>
            <h1>Generating Files with JavaScript</h1>
            <h2><a href="https://tutorialzine.com/2011/05/generating-files-javascript-php/">&laquo; Read and download on Tutorialzine</a></h2>
        </header>

        <form action="./" method="post">
            <textarea></textarea>
            <a href="#" class="blueButton" id="download">Download</a>
        </form>

        <footer>Another cool example: <a href="#" id="downloadPage">download this page.</a> <b>To download the source code, visit <a href="https://tutorialzine.com/2011/05/generating-files-javascript-php/">Tutorialzine.com</a></b></footer>

        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
        <script src="assets/js/jquery.generateFile.js"></script>
        <script src="assets/js/script.js"></script>

    </body>
</html>

The page uses the HTML5 doctype, as we are using some of the tags defined by the standard. For it to work in IE, we also need to include the HTML5 enabling script in the head section.

Before the closing body tag, we are adding the jQuery library, the generateFile plugin we will be writing in a moment, and the script.js file that listens for events and triggers the file downloads.

downloading-files-jquery-php.jpg

The PHP

As you probably know, generating files is not possible with JavaScript alone. Different solutions exist (some of them even relying on Flash), but using a generic PHP script on the backend provides better control and ease of use (not to mention that it works in every major browser out there).

You can see the generic file generation script below:

download.php

if(empty($_POST['filename']) || empty($_POST['content'])){
    exit;
}

// Sanitizing the filename:
$filename = preg_replace('/[^a-z0-9\-\_\.]/i','',$_POST['filename']);

// Outputting headers:
header("Cache-Control: ");
header("Content-type: text/plain");
header('Content-Disposition: attachment; filename="'.$filename.'"');

echo $_POST['content'];

What this PHP script does is simply add some headers on top of an echo statement. The plugin we are building must pass two parameters along with the POST request: filename and content. The script will print the content of the file, while setting three headers that will force the file download box to appear (instead of your browser simply opening it).

To use the plugin you need to upload this file somewhere on your server and pass its URL to the plugin we will be coding next.

The jQuery

As you saw in the previous section, our plugin has to issue a POST request to download.php. The natural choice for making a request would be by using AJAX. However, there is a shortcoming to using this method - it does not trigger the file download dialog to appear.

So what we need is a bit more old school. We will be dynamically creating a hidden iframe and write a form to it, which we will later submit via POST. The action attribute of the form points to download.php, so the file download dialog will pop up, exactly as we need it to.

Now lets lay down the jQuery code that does this:

assets/jquery.generateFile.js

(function($){

    // Creating a jQuery plugin:

    $.generateFile = function(options){

        options = options || {};

        if(!options.script || !options.filename || !options.content){
            throw new Error("Please enter all the required config options!");
        }

        // Creating a 1 by 1 px invisible iframe:

        var iframe = $('<iframe>',{
            width:1,
            height:1,
            frameborder:0,
            css:{
                display:'none'
            }
        }).appendTo('body');

        var formHTML = '<form action="" method="post">'+
            '<input type="hidden" name="filename" />'+
            '<input type="hidden" name="content" />'+
            '</form>';

        // Giving IE a chance to build the DOM in
        // the iframe with a short timeout:

        setTimeout(function(){

            // The body element of the iframe document:

            var body = (iframe.prop('contentDocument') !== undefined) ?
                            iframe.prop('contentDocument').body :
                            iframe.prop('document').body;   // IE

            body = $(body);

            // Adding the form to the body:
            body.html(formHTML);

            var form = body.find('form');

            form.attr('action',options.script);
            form.find('input[name=filename]').val(options.filename);
            form.find('input[name=content]').val(options.content);

            // Submitting the form to download.php. This will
            // cause the file download dialog box to appear.

            form.submit();
        },50);
    };

})(jQuery);

In less than 50 lines (with comments stripped) the above fragment does what we need. It creates a hidden iframe with a form inside it.

Notice the setTimeout() function. Without it we cannot access the document element of the iframe in Internet Explorer. This way, we are giving it time to build the DOM and make it available to us.

And here is how to use this plugin:

assets/script.js

$(document).ready(function(){

    $('#download').click(function(e){

        $.generateFile({
            filename    : 'export.txt',
            content     : $('textarea').val(),
            script      : 'download.php'
        });

        e.preventDefault();
    });

    $('#downloadPage').click(function(e){

        $.generateFile({
            filename    : 'page.html',
            content     : $('html').html(),
            script      : 'download.php'
        });

        e.preventDefault();
    });

});

When calling $.generateFile, you need to pass the name of the file (should be something descriptive), its text content, and the path to download.php. As you can see in the example above, we can generate any kind of file, as long as it is text.

With this our simple plugin is complete!

Conclusion

You can use this code to add export features to your web app or enhance certain areas of your site with download functionality. It is even possible to generate doc files and spreadsheets if you follow Microsoft Office's XML formats. The best part is that everything is done with JavaScript and you can easily combine different sources of data.

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

very useful tut. haven't tested it out yet, but it will be useful for a little project i'm working on.

cheers!

Miss-leading title. Add at the end: ...and PHP

Exactly, same thought here. And besides that this is also vulnerable for CSRF.

What's CSRF?

Cross-site request forgery. A common attack against websites. Wikipedia and OWASP have good information about it (just google 'csrf').

The back-end can be anything you want; with the script above, I used Sinatra on the back-end just fine.

Beben Koben

woh...this is cool
keep posted master :D

Sharun Kumar

Cool!!!!!

Dan Partac

Hey Martin,

This does not work for me and I need such a solution, please help me track the issue.

Danny

Martin Angelov

It doesn't work on the demo site here, or when you set it up on your host?
If it is the latter case, you will need to give a link to your page.

Dan Partac

Hi,

Thanks for taking your time to reply.

The script does not on my local computer. Steps to replicate:

  • Downloaded the package you provided,
  • unpacked the archive to my desktop,
  • double clicked the index.html file

I also downloaded full page of your live demo, same problem.

Maybe it is supposed to work only from web live sites and not from local machines?

The thing is, I need to implement this into a little app and I need to test it, so it must work on localhost environment, both standalone and as part of my application.

To solve this matter, I can provide you with a Teamviewer connection and check for yourself.

Regards and many thanks,
Danny

Hi. PHP doesn't work offline. Try installing a web server or uploading to a PHP compatible server.

Dan Partac

Can you help me mate?

Keep in mind under localhost environment you will need to have a webserver running that's capable of serving PHP (e.g. apache). You can't simply unzip the package, double click on index.html and expect it to work.

Dan Partac

Thanks mate. That was it. Before I was trying to get it to work on my desktop.

Now moved to my htdocs, browsing

http://localhost/generate/index.html

Works! Thank you so much.
Danny

Dan Partac

Implemented in my application (still on localhost) it gives me this error

iframe.prop is not a function
var body = (iframe.prop('contentDocument') !== undefined) ?

Using jQuery 1.5.2. Any ideas?

Thanks again,
Danny

Dan Partac

the prop() was introduced in jQuery 1.6.0 and could not work with my 1.5.2 version.

Now everything works perfect.

Thanks again

Dan Partac

TIP: download.php can be changed to support different file formats:
http://davidwalsh.name/php-header-mime

:P

Sandeepan Kundu

Well this is working really nice.
I have a concern with respect to clearing of the appended iframe form the DOM.

How can i, or can the plugin be customized to reuse the same iframe ?

Sandeepan Kundu

Well the following code, worked:

        var iframe = document.getElementById('downloadiframe');

        if (!iframe) {
            iframe = $('<iframe>', {
                width: 1,
                id: 'downloadiframe',
                height: 1,
                frameborder: 0,
                css: {
                    display: 'none'
                }
            }).appendTo('body');
        }
        else {
            iframe = $('#downloadiframe');
        }

on the next stage another issue is faced in IE. The popup blocker is resetting the entire page while download is getting initiated and if we accpet the download.
Any solution or clue !!!!

Hallo,
First i say really great script..
How can i make this for download pdf file?

Thank you
Wiyono

I have multiple text areas on the page, I want to have more than one download link but they all just download the content of the first textarea. How do I get around this? Thanks.

Hey Gavin,
If you want multiple buttons then you need to start in the index.html by giving each button you wanted a different id. You would then give each different text area you wanted an id as well.

In the script.js you would then create copies of the following:

$('#download').click(function(e){

        $.generateFile({
            filename    : 'export.txt',
            content     : $('textarea').val() + test5 + test,
            script      : 'download.php'
        });

        e.preventDefault();
    });

updating the #download with the id for the new button and the ('textarea') with the new id for the text area. I actually assigned it to a variable when I tested it out with a text box:

var test5 = document.getElementById('textarea1').value;

Hope that helps.

Ankur Mhatre

Thank for this great script. Will use on my site.

First of all, great script.

I use it to generate a spreadsheet file.
Because this process takes a while, I put a loading animated gif on my page.

I want to remove loader before the download pup-up appears.
Is there an event for this and if so, how can it trigger my callback routine?

Hopefully somebody can help.

Nice & Clean Script,

I want to generate JSON data file with this script, is it possible?

i try the code and it works, now how to add the image loading while do some processing in download.php then hide the image when the process is finished? ty

is it possible to make this with jsp not with php ?

I just adapted it in jsp

<%
    if (!request.getParameter("filename").isEmpty() && !request.getParameter("content").isEmpty()) {
        String filename = request.getParameter("filename");
        response.addHeader("Cache-Control", "");
        response.addHeader("Content-type", "text/plain");
        response.addHeader("Content-Disposition", "attachment; filename=" + filename);
        out.print(request.getParameter("content"));
        out.flush();
    }
%>

i want to download c language file(extension .c) instead of text file( .txt) file...
what changes should i do?

Hi Martin,
first of all thanks for sharing the tut, it was really helpful. I would like to know if it is possible to Set a specific directory for download file? I am using this on my local host and i would like to set a directory like "D//MyApp" foe all downloaded files (Not in Browsers's Default Download Folder).
Can you please let me know if this is possible or not?

How about setting the name to a path (like your example: D:/MyApp)

Cheers! got this to work in an exisiting project in just 5 minutes :)

Thanks for the insights

What if I want to encrypt the content before download?