【问题标题】:Exporting Using HttpContext.Current.Response is Causing Issues使用 HttpContext.Current.Response 导出会导致问题
【发布时间】:2016-09-08 09:36:04
【问题描述】:

我正在使用 asp.net/C#/HTML5/bootstrap 开发一个网站。要求之一是将文档导出到 Excel 和/或 PDF。我可以使用以下 sn-p(这是 Excel sn-p)导出(成功):

        HttpContext.Current.Response.ContentType = "application/octet-stream";
        HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.UTF8;
        HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + filename);
        HttpContext.Current.Response.BinaryWrite(xlsBytes);
        HttpContext.Current.Response.Flush();
        HttpContext.Current.Response.End();

我遇到的问题是,在此运行之后,它似乎正在停止页面生命周期,使其停滞不前。例如,用户单击导出按钮,该按钮调用 javascript 并抛出“请稍候”模式对话框并提交表单:

<script src="../Scripts/waitingFor.js"></script>

<script type="text/javascript">
    function pleaseWait() {
        waitingDialog.show("Building File<br/>...this could take a minute", { dialogSize: "sm", progressType: "warning" });

        form = document.getElementById("frm_contentMaster");
        form.submit();
    }
</script>

javascript 包含文件:

/**
 * Module for displaying "Waiting for..." dialog using Bootstrap
 *
 * @author Eugene Maslovich <ehpc@em42.ru>
 */

(function (root, factory) {
    'use strict';

    if (typeof define === 'function' && define.amd) {
        define(['jquery'], function ($) {
            return (root.waitingDialog = factory($));
        });
    }
    else {
        root.waitingDialog = root.waitingDialog || factory(root.jQuery);
    }
}(this, function ($) {

'use strict';

/**
 * Dialog DOM constructor
 */
function constructDialog($dialog) {
    // Deleting previous incarnation of the dialog
    if ($dialog) {
        $dialog.remove();
    }
    return $(
        '<div id="waitingFor" class="modal fade" data-backdrop="static" data-keyboard="false" tabindex="-1" role="dialog" aria-hidden="true" style="padding-top:15%; overflow-y:visible;">' +
            '<div class="modal-dialog modal-m">' +
                '<div class="modal-content">' +
                    '<div class="modal-header" style="display: none;"></div>' +
                    '<div class="modal-body">' +
                        '<div class="progress progress-striped active" style="margin-bottom:0;">' +
                            '<div class="progress-bar" style="width: 100%"></div>' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>' +
        '</div>'
    );
}

// Dialog object
var $dialog;

return {
    /**
     * Opens our dialog
     * @param message Custom message
     * @param options Custom options:
     *   options.headerText - if the option is set to boolean false, 
     *     it will hide the header and "message" will be set in a paragraph above the progress bar.
     *     When headerText is a not-empty string, "message" becomes a content 
     *     above the progress bar and headerText string will be set as a text inside the H3;
     *   options.headerSize - this will generate a heading corresponding to the size number. Like <h1>, <h2>, <h3> etc;
     *   options.headerClass - extra class(es) for the header tag;
     *   options.dialogSize - bootstrap postfix for dialog size, e.g. "sm", "m";
     *   options.progressType - bootstrap postfix for progress bar type, e.g. "success", "warning";
     *   options.contentElement - determines the tag of the content element. 
     *     Defaults to "p", which will generate a <p> tag;
     *   options.contentClass - extra class(es) for the content tag.
     */
    show: function (message, options) {
        // Assigning defaults
        if (typeof options === 'undefined') {
            options = {};
        }
        if (typeof message === 'undefined') {
            message = 'Loading';
        }
        var settings = $.extend({
            headerText: '',
            headerSize: 3,
            headerClass: '',
            dialogSize: 'm',
            progressType: '',
            contentElement: 'p',
            contentClass: 'content',
            onHide: null // This callback runs after the dialog was hidden
        }, options),
        $headerTag, $contentTag;

        $dialog = constructDialog($dialog);

        // Configuring dialog
        $dialog.find('.modal-dialog').attr('class', 'modal-dialog').addClass('modal-' + settings.dialogSize);
        $dialog.find('.progress-bar').attr('class', 'progress-bar');
        if (settings.progressType) {
            $dialog.find('.progress-bar').addClass('progress-bar-' + settings.progressType);
        }

        // Generate header tag
        $headerTag = $('<h' + settings.headerSize + ' />');
        $headerTag.css({ 'margin': 0 });
        if (settings.headerClass) {
            $headerTag.addClass(settings.headerClass);
        }

        // Generate content tag
        $contentTag = $('<' + settings.contentElement + ' />');
        if (settings.contentClass) {
            $contentTag.addClass(settings.contentClass);
        }

        if (settings.headerText === false) {
            $contentTag.html(message);
            $dialog.find('.modal-body').prepend($contentTag);
        }
        else if (settings.headerText) {
            $headerTag.html(settings.headerText);
            $dialog.find('.modal-header').html($headerTag).show();

            $contentTag.html(message);
            $dialog.find('.modal-body').prepend($contentTag);
        }
        else {
            $headerTag.html(message);
            $dialog.find('.modal-header').html($headerTag).show();
        }

        // Adding callbacks
        if (typeof settings.onHide === 'function') {
            $dialog.off('hidden.bs.modal').on('hidden.bs.modal', function () {
                settings.onHide.call($dialog);
            });
        }
        // Opening dialog
        $dialog.modal();
    },
    /**
     * Closes dialog
 */
    hide: function () {
        if (typeof $dialog !== 'undefined') {
            $dialog.modal('hide');
        }
    }
};
}));

我使用 NPOI 在后面的代码中创建 Excel 文件(简化函数):

using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

protected void exportExcel()
{
    XSSFWorkbook wb = new XSSFWorkbook();

    XSSFSheet sh = (XSSFSheet)wb.CreateSheet("Legend");

    //*****************************************
    //* Workbook Download & Cleanup
    //*****************************************
    MemoryStream stream = new MemoryStream();
    wb.Write(stream);
    stream.Dispose();

    var xlsBytes = stream.ToArray();
    string filename = "Behavior Stats YTD.xlsx";

    MemoryStream newStream = new MemoryStream(xlsBytes);

    HttpContext.Current.Response.ContentType = "application/octet-stream";
    HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.UTF8;
    HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + filename);
    HttpContext.Current.Response.BinaryWrite(xlsBytes);
    HttpContext.Current.Response.Flush();
    HttpContext.Current.Response.End();
}

这会创建 Excel 文件并将其推送给用户,但后面代码的生命周期不会继续——它会在 End 命令后立即停止。如果我注释掉 HttpContext 行,显然 Excel 工作表并没有被创建,但页面的生命周期仍在继续——后面的其余代码运行,页面刷新,模式请等待对话框消失。

那我用错了吗?我见过的大多数关于如何导出的示例都使用这种方法。还有其他更清洁和/或更安全的出口方式吗?我需要做一个简单的调整,让生命周期继续吗?谁创造了液体肥皂,为什么?

您能提供的任何帮助将不胜感激。

【问题讨论】:

    标签: asp.net excel download pdf-generation httpcontext


    【解决方案1】:

    它完全按照应有的方式工作。你告诉浏览器响应是一个文件。响应一次只能是一件事。您不能在同一响应中包含页面内容和文件。

    您可以通过将文件下载到 IFrame 中来将两者分开。首先把你的文件下载 C# 代码放在它自己的页面中。然后使用 JavaScript 函数从 IFrame 调用该页面。

    function DownloadExcel() {
        var downloadFrame = document.createElement("IFRAME");
    
        if (downloadFrame != null) {
            downloadFrame.setAttribute("src", '/DownloadExcel.aspx');
            downloadFrame.style.width = "0px";
            downloadFrame.style.height = "0px";
            document.body.appendChild(downloadFrame);
        }
    }
    

    【讨论】:

    • 真心感谢你,这让我头疼了很长时间,你的解决方案简单而优雅。我也很欣赏对响应内容的解释——出于某种原因,这让我脑子里“咔哒”一声,我明白为什么它不起作用了。再次感谢并祝您编码愉快!
    猜你喜欢
    • 2023-03-05
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多