Servlet响应包含文本(用于显示)以及文件下载

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

本文关键字:文件下载 显示 用于 响应 包含 文本 Servlet      更新时间:2023-09-26

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

但我想通过Javascript调用servlet 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如果您认为可以用更好的方式完成

经过一段时间的努力,我找到了一个有效的解决方案。也许还有更好的,但这就是我想出的。

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

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

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


因此,事不宜迟,这里我们使用。。。代码:)

解决方案:

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转储包含"小写"&我不得不替换的"大写"引号。我把每一个链接都放在这里,以防有人也与他们斗争。它们对那里列出的那些字符有多个编码。

Unicode字符"双低位-9引号"(U+201E)

Unicode字符"左双引号"(U+201C)