首先,大家都知道 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 对象来实现。