包含文本(用于显示)以及文件下载的 Servlet-Response

Servlet-Response containing text (for display) as well as file download

我正在尝试通过 Java Servlet 从我的服务器下载文件。 我遇到的问题是,当我直接输入 servlet url (https://localhost:8443/SSP/settings?type=db_backup) 时,我让 servlet 执行它的代码并提示我下载对话框。

但我想通过 Javascript 调用 servlets doGet 方法,用某种进度条包装它。

这里的问题:执行了 servlet 中的代码,但我没有收到文件的下载提示。

到目前为止我的代码:

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();
}

解决方案 #2 效果很好,我收到了一个下载文件的弹出窗口。

解决方案 #1 调用 servlet doGet 方法(通过上面的 JS 代码,我的 servlet 中的代码被正确执行)但我没有得到下载弹出窗口

我想使用解决方案 #1,因为这让我有机会用加载动画包装 $.post 调用。

我在解决方案 #1 中缺少什么来获取下载弹出窗口?

编辑 1:

我发现$.get()函数中的data填充的是想要的文件的内容。例如,我现在可以在 div 中显示 .txt 文件的内容,但我想改为下载所述 .txt 文件。

编辑 2:

已解决,详情请参阅下面的回答 & comment/ansewer 如果您认为可以用更好的方式完成

经过相当长的一段时间尝试让它工作后,我找到了一个可行的解决方案。可能有更好的,但那是我想出的。

希望这对其他人也有帮助。

我在这里所做的基本解释:

  • 让表单对 java servlet
  • 执行 GET 请求(通过 JS)
  • servlet 执行命令行脚本(在我的例子中是我的 postgreSQL 数据库的 sql- 转储)
  • servlet 收集命令行的输出和生成文件的内容(sql_dump)并将它们放入响应中
  • 客户端获取响应并将其分成 3 部分(命令行输出、sql_dump-文件的文件名和内容)
  • 然后(通过 JS)命令行输出显示在文本区域中,以便更好地了解脚本实际执行的操作
  • sql_dump 文件的内容由 JS 代码处理以生成要下载的文件(通过按钮手动或自动)


因此,不用多说,这里我们使用 flow ... 代码 :)

解决方案:

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 转储包含 "lowercase" & "uppercase" 我必须替换的引号。我在这里给每个人都放了一个 link 以防有人也与他们斗争。他们对那里列出的那些字符有多种编码。

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

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