【.NET Asp WebForm】使用 Ajax (ScriptManager + UpdatePanel)实现无跳转下载

686 阅读1分钟

首先,大家都知道 Ajax 本身是不能下载文件的,因为官方文档说 Ajax 只能传回 String 类型的报文。

既然不是二进制或者blob,就无法触发浏览器的下载机制。所以大家都说 Ajax 不能下载文件。

然而,我们又知道 Aspx 的 WebForm 中,我们通常会大量使用 ScriptManage + UpdatePanel 的形式对页面进行局部的异步更新。

所以,如何实现下载呢?首先要知道:

Asp 的工作原理

ScriptManager 在运行初始化 Init 函数的时候,会把 UpdatePanel 中每个控件都制成可以接收后端 CS 传过来的 Ajax Response,并且通过页面上注册的 ScriptManager,转换成 JS 代码,最后在浏览器运行这段 JS 代码,造成大家看到 Asp 中浏览器可以直接运行 CS 代码的假象。

我们如果用 Chrome 看一下 CS 代码传到前端的 Ajax Response,会觉得匪夷所思,不全是 Html ,类似下面这种:

1|#||4|15332|updatePanel|ContentPlaceHolder1_UpdatePanel1|
            <div>
                <div class="header theme-color">
                    ...
                    ...

上面是一段头,下面是 UpdatePanel 本身的 HTML,当然是后端 CS 代码更新过的。

前端注册好的 ScriptManager,根据头部的信息,来定点渲染下面的 html 代码,达成局部刷新的效果。

如果你把 Response 拉到最下面,会看到

	...
    ...
</div>
        |0|hiddenField|__EVENTTARGET||0|hiddenField|__EVENTARGUMENT||0|hiddenField|__LASTFOCUS||4320|hiddenField|__VIEWSTATE|/wEPDwUKMTk2NzE2NDQ0Mw9kFgJmD2QWAgIDD2QWAgIBD2QWAgIDD2QWAmYPZBYWAgEPDxYCHgRUZXh0BUvlv4PnkIblgaXlurflm6LkvZPmtYvor4Tns7vnu58gLSDmiqXl
        ...
|237|scriptStartupBlock|ScriptContentNoTags|Sys.Application.add_init(function() {
    $create(Sys.Extended.UI.ConfirmButtonBehavior, {"ConfirmText" ...
        ...

这就是传说中的 HiddenField 和 ViewState 了。

还有下面的 JS,也就是 JQuery 中的 Ajax 的 success 代码段运行的 JS 了。

是不是很酷炫!

但是,很可惜,这长段长段的 ViewState 和 整段 Html 造成了 Response 过大的问题。在大型网站中会有明显的传输效率问题。

虽然 html 段的长度可以通过分割多个 UpdatePanel 来解决,但是 ViewState 的长度确实致命的,这也是 WebForm 后来渐渐被抛弃的一个主要原因之一。

如何使用 JS 下载文件

我们知道了 ScriptManager 是怎么回事,问题就变成了,如果使用纯的 JQuery 的 Ajax,如何下载文件呢?

答案就是,使用 Anchor 标签的 download 属性。

直接上 JS:

function downloadFile(webFilePath, downloadFileName) {
    var createdAnchor = document.createElement('a');
    createdAnchor.setAttribute('href', webFilePath);
    createdAnchor.setAttribute('download', downloadFileName);
    createdAnchor.click();
}

用 createElement 创建一个 anchor 标签 (写过 React Render 的小伙伴是不是很熟悉),只要 href 中能获取到这个静态文件,就可以实现下载。

把这个 downloadFile 函数放到 JQuery Ajax 的 success 代码段就可以了!

结合 Asp

最后的答案就很简单了。

在 Asp 的 Button Click 事件中,从后端获取信息,直接放入 JS 的这个函数,就行了!

protected void ButtonQrDownload_Click(object sender, EventArgs e)
{
    var groupName = txtGrpName.Text;
    var imgUrl = ImgQRCode.ImageUrl;
    imgUrl = imgUrl.Replace("\\", "/");
    PageUtils.ExecuteScript(UpdatePanel1, $"downloadFile('{imgUrl}', 'QRCode_{groupName}')");
}

这边要注意的就是 CS 中的路径是反斜杠,而 Url 中是正斜杠,可以通过 Replace 或者创建 URI 对象来实现。