0

I'm trying to download a file from my server through a Java Servlet. The Problem I have is that when I enter the servlet url directly (https://localhost:8443/SSP/settings?type=db_backup) I get the servlet to execute its code and prompt me with a download dialog.

But I would like to call the servlets doGet method via Javascript to wrap it with a progress bar of some kind.

Problem here: Code in servlet is executed but I dont get the download prompt for the file.

My Code so far:

HTML:

<!-- Solution #1 -->
<button class="btn_do_db_backup" type="button">DB-Backup #1</button>

<!-- Solution #2 -->
<form action="/SSP/settings?type=db_backup" method="GET">
    <button type="submit">DB-Backup #2</button></br>
</form>

JS:

// Solution #1
$(".btn_do_db_backup").click(function(e){
    e.preventDefault();
    $.get("settings?type=db_backup", function(data){
        if(data != ""){
        //further coding
        }
    });

    // Having the code below works but doesnt
    // give me the chance to wrap the call with a loading animation

    //document.location = "/SSP/settings?type=db_backup";
});

Servlet:

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    // PART 1 
    // execute srcipt to generate file to download later on

    StringBuffer output = new StringBuffer();
    ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "D:\\TEMP\\sql_dump.cmd");
    builder.redirectErrorStream(true);
    Process p = builder.start();
    BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String line = "";
    String filename = "";
    int tmp = 0;
    while (true) {
        line = r.readLine();
        if (line == null) { break; }
        output.append(line + "\n");
        // code for finding filename not optimal but works for now -> redo later on
        if(tmp == 1){
            filename = line.substring(line.indexOf("db_backup_"), line.indexOf('"', line.indexOf("db_backup_")) );
        }
        tmp++;
    }

    // PART 2
    // download the file generated above

    OutputStream out = response.getOutputStream();
    String filepath = "D:\\TEMP\\sql_dump\\";

    response.setContentType("APPLICATION/OCTET-STREAM");
    response.setHeader("Content-Disposition", "attachment;filename=\"" + filename + "\"");

    FileInputStream fileInputStream = new FileInputStream(filepath + filename);

    int i;
    while ((i = fileInputStream.read()) != -1) {
        out.write(i);
    }

    out.close();
    fileInputStream.close();
}

Solution #2 works great, I get a popup to download the file.

Solution #1 calls the servlets doGet-method (via the above JS-Code and the code from my servlet is executed correctly) but I dont get a download popup

I would like to go with solution #1 though as this gives me the opportunity to wrap the $.post call with a loading animation.

What am I missing within solution #1 to get that download popup to shop up?

EDIT 1:

I found that data in the $.get() function is filled with the content of the desired file. I can now display the content of a .txt file in a div for example but I would like to donwload said .txt file instead.

EDIT 2:

Solved it, see my answer below for details & comment/ansewer if you think it can be done in a better way

Alkahna
  • 441
  • 7
  • 21
  • yeah I trid different methods, like the ones suggested here: [http://stackoverflow.com/questions/3749231/download-file-using-javascript-jquery](http://stackoverflow.com/questions/3749231/download-file-using-javascript-jquery) – Alkahna Aug 02 '16 at 13:45
  • Of course you do not get a “safe as” dialog for AJAX requests – it’s a _background_ request. – CBroe Aug 05 '16 at 10:26
  • is there any other way to get the dialog by going to JS code first so I can wrap the request with a loading animation? – Alkahna Aug 05 '16 at 10:46

1 Answers1

0

after quite some time trying to get it to work I found a solution that works. There may be better ones but thats the one I came up with.

Hope this may be helpfull for others as well.

Basic explanation of what I did here:

  • Have a form do a GET-Request (via JS) to a java servlet
  • The servlet executes a commandline script (in my case a sql-dump of my postgreSQL DB)
  • The servlets gathers the output from the commandline and the contents of the generated file (the sql_dump) and puts them in the response
  • The client gets the response and cuts it into 3 pieces (commandline output, filename & contents of sql_dump-file)
  • Then (via JS) the commandline output is shown in a textarea for a better overview of what the script actually did
  • The contents of the sql_dump-file is processed by JS-Code to generate a file to download (eihter manually via a button or automatically)


So without further ado, here we go with the flow ... code :)

SOLUTION:

HTML:

<form id="form_download_db_backup">
    <input type="submit" value="Create & Download DB-Backup"></br>
    <a download="" id="downloadlink" style="display: none">download</a>
</form>
<div class="db_backup_result" id="db_backup_result" style="display: none;">
    </br>Commandline-Output</br>
    <textarea id ="txta_db_backup_result" rows="4" cols="50"></textarea>
</div>

JS:

$("#form_download_db_backup").submit(function(e){
    e.preventDefault();
    var spinner = new Spinner().spin();
    var target = document.getElementById('content');
    target.appendChild(spinner.el);
    $.ajax({
        url:'settings?type=db_backup',
        type:'get',
        success:function(data){
            spinner.stop();
            if(data != ""){
                var str_data = "" + data;

                // Cut commanline output from data
                var commandline_output = str_data.substring( 0, str_data.indexOf("--End") );

                //show commanline output in textarea
                $("#txta_db_backup_result").html(commandline_output);

                // Cut content of db_backup file from data
                var sql_dump_content = str_data.substring( str_data.indexOf("--sql_d_s--") + 13,str_data.indexOf("--sql_d_e--") );//|

                // Cut filename from data
                var filename = str_data.substring( str_data.indexOf("--sql_d_fns--") + 15,str_data.indexOf("--sql_d_fne--") - 2 );

                //-------------------------------------------------------------
                // Prepare download of backupfile
                var link = document.getElementById('downloadlink');
                var textFile = null;
                var blob_data = new Blob([sql_dump_content], {type: 'text/plain'});

                // FOR IE10+ Compatibility
                if(window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveBlob(blob_data, filename);
                }

                // If we are replacing a previously generated file we need to
                // manually revoke the object URL to avoid memory leaks.
                if (textFile !== null) {
                  window.URL.revokeObjectURL(textFile);
                }
                textFile = window.URL.createObjectURL(blob_data);
                link.href = textFile;
                link.download = filename;
                //link.style.display = 'block'; // Use this to make download link visible for manual download
                link.click(); // Use this to start download automalically
                //-------------------------------------------------------------

                // show div containing commandline output & (optional) downloadlink
                document.getElementById("db_backup_result").style.display = 'block';
            }
        }
    });
});

Java-Servlet:

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    String type = request.getParameter("type");

    if(null != type)switch (type) {
        case "db_backup":

            ServletOutputStream out = response.getOutputStream();

            // Prepare multipart response
            response.setContentType("multipart/x-mixed-replace;boundary=End");

            // Start: First part of response ////////////////////////////////////////////////////////////////////////

            // execute commandline script to backup the database
            ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "D:\\TEMP\\sql_dump.cmd");
            builder.redirectErrorStream(true);
            Process p = builder.start();
            BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line = "";
            String filename = "";
            int tmp = 0;

            while (true) {
                line = r.readLine();
                if (line == null) { break; }
                // code for finding filename not optimal but works for now -> redo later on
                if(tmp == 1){
                    filename = line.substring(line.indexOf("db_backup_"), line.indexOf('"', line.indexOf("db_backup_")) );
                }
                else{
                    line = line.replace("\u201E", "\'"); // replaces the lowercase " (DOUBLE LOW-9 QUOTATION MARK)
                    line = line.replace("\u201C", "\'"); // replaces the uppercase " (LEFT DOUBLE QUOTATION MARK)
                }
                out.println(line);
                tmp++;
            }

            // End: First part of response ////////////////////////////////////////////////////////////////////////

            // Separator of firt & second part
            out.println("--End");
            out.flush();

            // Add filename in response (name of download file)
            out.println("--sql_d_fns--"); // separator for filename (used to extract filename from response data)
            out.println(filename);
            out.println("--sql_d_fne--"); // separator for filename (used to extract filename from response data)

            // Start: Second part of response ////////////////////////////////////////////////////////////////////////
            out.println("--sql_d_s--"); // separator for content of db-dump (this is the text thats going to be downloaded later on)
            String filepath = "D:\\TEMP\\sql_dump\\";

            FileInputStream fileInputStream = new FileInputStream(filepath + filename);

            int i;
            while ((i = fileInputStream.read()) != -1) {
                out.write(i);
            }
            out.println("--sql_d_e--"); // separator for content of db-dump (this is the text thats going to be downloaded later on)
            // End: Second part of response ////////////////////////////////////////////////////////////////////////

            // End the multipart response
            out.println("--End--");
            out.flush();
            break;
        default:
            break;
    }
}

postgreSQL dump contain "lowercase" & "uppercase" quotation marks which I had to replace. I put a link to each here in case someone struggles with them as well. They have multiple encodings for those characters listed there.

Unicode Character 'DOUBLE LOW-9 QUOTATION MARK' (U+201E)

Unicode Character 'LEFT DOUBLE QUOTATION MARK' (U+201C)

Alkahna
  • 441
  • 7
  • 21