让UpdatePanel支持文件上传(2):服务器端组件

2024-06-04 20:57:42
我们现在来关注服务器端的组件。目前的主要问题是,我们如何让页面(事实上是ScriptManager控件)认为它接收到的是一个异步的回送?ScriptManager控件会在HTTP请求的Header中查找特定的项,但是我们在向IFrame中POST数据时无法修改Header。所以我们必须使用一个方法来“欺骗”ScriptManager。

目前使用的解决方案是,我们在POST数据之前在页面中隐藏的输入元素(<input type="hidden" />)中放入一个特定的标记,然后我们开发的服务器端组件(我把它叫做AjaxFileUplaodHelper)会在它的Init阶段(OnInit方法)中在Request Body中检查这个标记,然后使用反射来告诉ScriptManager目前的请求为一个异步请求。
但是事情并不像我们想象的那么简单,让我们在写代码之前来看一个方法:

PageRequestManager.OnInit

internal sealed class PageRequestManager
{// ...internal void OnInit(){if (_owner.EnablePartialRendering && !_owner._supportsPartialRenderingSetByUser){IHttpBrowserCapabilities browser = _owner.IPage.Request.Browser;bool supportsPartialRendering =(browser.W3CDomVersion >= MinimumW3CDomVersion) &&(browser.EcmaScriptVersion >= MinimumEcmaScriptVersion) &&browser.SupportsCallback;if (supportsPartialRendering){supportsPartialRendering = !EnableLegacyRendering;}_owner.SupportsPartialRendering = supportsPartialRendering;}}// ...
}

  上面这段代码会在ScriptManager的OnInit方法中被调用。请注意红色部分的代码,“_owner”变量是当前页面上的ScriptManager。在页面受到一个真正的异步会送之后,PageRequestManager会响应页面的Error事件,并且将错误信息用它定义的格式输出。如果我们只是修改了ScriptManager的私有field,那么如果在异步回送时出现了一个未捕获的异常,那么页面就会输出客户端未知的内容,导致在客户端解析失败。所以我们必须保证这种情况下的输出和真正的异步回送是相同的,所以我们就可以使用以下的做法来解决错误处理的问题。

代码实现

internal static class AjaxFileUploadUtility
{internal static bool IsInIFrameAsyncPostBack(NameValueCollection requestBody){ string[] values = requestBody.GetValues("__AjaxFileUploading__");if (values == null) return false;foreach (string value in values){if (value == "__IsInAjaxFileUploading__"){return true;}}return false;}// ...
}
[PersistChildren(false)]
[ParseChildren(true)]
[NonVisualControl]
public class AjaxFileUploadHelper : Control
{// ScriptManager members;private static FieldInfo isInAsyncPostBackFieldInfo;private static PropertyInfo pageRequestManagerPropertyInfo;// PageRequestManager members;private static MethodInfo onPageErrorMethodInfo;private static MethodInfo renderPageCallbackMethodInfo;static AjaxFileUploadHelper(){Type scriptManagerType = typeof(ScriptManager);isInAsyncPostBackFieldInfo = scriptManagerType.GetField("_isInAsyncPostBack",BindingFlags.Instance | BindingFlags.NonPublic);pageRequestManagerPropertyInfo = scriptManagerType.GetProperty("PageRequestManager",BindingFlags.Instance | BindingFlags.NonPublic);Assembly assembly = scriptManagerType.Assembly;Type pageRequestManagerType = assembly.GetType("System.Web.UI.PageRequestManager");onPageErrorMethodInfo = pageRequestManagerType.GetMethod("OnPageError", BindingFlags.Instance | BindingFlags.NonPublic);renderPageCallbackMethodInfo = pageRequestManagerType.GetMethod("RenderPageCallback", BindingFlags.Instance | BindingFlags.NonPublic);}public static AjaxFileUploadHelper GetCurrent(Page page){return page.Items[typeof(AjaxFileUploadHelper)] as AjaxFileUploadHelper;}private bool isInAjaxUploading = false;protected override void OnInit(EventArgs e){base.OnInit(e);if (this.Page.Items.Contains(typeof(AjaxFileUploadHelper))){throw new InvalidOperationException("One AjaxFileUploadHelper per page.");}this.Page.Items[typeof(AjaxFileUploadHelper)] = this;this.EnsureIsInAjaxFileUploading();}private void EnsureIsInAjaxFileUploading(){this.isInAjaxUploading =             AjaxFileUploadUtility.IsInIFrameAsyncPostBack(this.Page.Request.Params);if (this.isInAjaxUploading){isInAsyncPostBackFieldInfo.SetValue(ScriptManager.GetCurrent(this.Page),true);this.Page.Error += new EventHandler(Page_Error);}}private void Page_Error(object sender, EventArgs e){// ...}private object _PageRequestManager;private object PageRequestManager{get{if (this._PageRequestManager == null){this._PageRequestManager = pageRequestManagerPropertyInfo.GetValue(ScriptManager.GetCurrent(this.Page), null);}return this._PageRequestManager;}}// ...
}

  这段实现并不复杂。如果Request Body中的“__AjaxFileUploading__”的值为“__IsInAjaxFileUploading__”,我们就会使用反射修改ScirptManager控件中的私有变量“_isInAsyncPostBack”。此后,我们使用了自己定义的Page_Error方法来监听页面的Error事件,当页面的Error事件被触发时,我们定义的新方法就会将能够正确解析的内容发送给客户端端。
自然,AjaxFileUploadHelper也需要将程序集中内嵌的脚本文件注册到页面中。我为组件添加了一个开关,可以让用户开发人员使用编程的方式来打开/关闭对于AJAX文件上传的支持。这部分实现更为简单:

注册脚本文件

public bool SupportAjaxUpload
{get { return _SupportAjaxUpload; }set { _SupportAjaxUpload = value; }
}
protected override void OnPreRender(EventArgs e)
{base.OnPreRender(e);if (this.isInAjaxUploading){this.Page.SetRenderMethodDelegate(new RenderMethod(this.RenderPageCallback));}if (this.Page.IsPostBack || !this.SupportAjaxUpload) return;if (!ScriptManager.GetCurrent(this.Page).IsInAsyncPostBack){ScriptReference script = new ScriptReference("Jeffz.Web.AjaxFileUploadHelper.js", this.GetType().Assembly.FullName);ScriptManager.GetCurrent(this.Page).Scripts.Add(script);}
}

  如果用户希望关闭对于AJAX文件上传的支持,他可以使用下面的代码将页面上AjaxFileUploadHelper控件的SupportAjaxUpload属性关闭:

关闭AJAX上传支持

AjaxFileUploadHelper.GetCurrent(this.Page).SupportAjaxUpload = false;

  等一下,这是什么?我是指在“OnPreRender”方法中的代码:

截获输出方式

if (this.isInAjaxUploading)
{this.Page.SetRenderMethodDelegate(new RenderMethod(this.RenderPageCallback));
}

  解释如下:在ScirptManager的“OnPreRender”方法执行时,页面的Render方法会被服务器端PageRequestManager类的RenderPageCallback方法替代。上面代码的作用是在“我们的”异步回送时,再次使用我们定义的方法来替换页面的Render方法。请注意之前的Page_Error方法也是我们重新定义的方法,当异步回送时遇到了未捕获的异常时会使用它来输出,请注意下面的代码:

自定义的输出方法

private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
{AjaxFileUploadUtility.WriteScriptBlock(this.Page.Response, true);StringBuilder sb = new StringBuilder();HtmlTextWriter innerWriter = new HtmlTextWriter(new StringWriter(sb));renderPageCallbackMethodInfo.Invoke(this.PageRequestManager, new object[] { innerWriter, pageControl });writer.Write(sb.Replace("*/", "*//*").ToString());AjaxFileUploadUtility.WriteScriptBlock(this.Page.Response, false);
}
private void Page_Error(object sender, EventArgs e)
{AjaxFileUploadUtility.WriteScriptBlock(this.Page.Response, true);onPageErrorMethodInfo.Invoke(this.PageRequestManager, new object[] { sender, e });AjaxFileUploadUtility.WriteScriptBlock(this.Page.Response, false);
}

  究竟什么是“AjaxFileUploadUtility.WriteScriptBlock”方法呢?我们为什么要这样写?其实这么做的目的是为了兼容各种浏览器,使它们都能够正确通过iframe正确收到服务器端获得的信息。这可以说是整个项目中最有技巧的部分了,我将会使用一个部分来单独讲一下这部分的机制。

转载于:https://blog.51cto.com/jeffz/59907

让UpdatePanel支持文件上传(2):服务器端组件相关推荐

  1. 让UpdatePanel支持文件上传(4):数据传输与解析机制

    现在就要开始整个项目中最有技巧的部分了.如果我们的组件需要在多种浏览器中正常的运行,我们必须好好考虑一下发送和解析数据的方式.如果我们把这部分的机制完全交给ASP.NET AJAX原有的行为来执行,则 ...

  2. 如何使用apiPOST进行模拟发送get、post、delete、put请求(支持文件上传)

    现在的模拟发送请求插件很多,但亲测apiPOST更好用一些,因为它不仅可以模拟发送get.post.delete.put请求,还可以导出文档,中文界面更适合国内的程序员. 今天来分享如何使用apiPO ...

  3. 让nginx支持文件上传的几种模式

    2019独角兽企业重金招聘Python工程师标准>>> 文件上传的几种不同语言和不同方法的总结. 第一种模式 : PHP 语言来处理 这个模式比较简单, 用的人也是最多的, 类似的还 ...

  4. 安卓开发8-WebView支持文件上传

    安卓手机中采用webview访问OA系统,当OA中使用input=file的方式时,点选择文件没有反应,需要在WebChromeClient中增加openFileChooser方法:chrome浏览器 ...

  5. EXTjs 同时支持文件上传和图片上传的htmleditor

    截图: 参照StarHtmleditor的源代码,自己又添加了文件上传功能.源代码如下: Szj_StarHtmleditor.js文件源码: [code] var HTMLEditor = Ext. ...

  6. SpringBoot整合阿里云OSS,支持文件上传、下载、删除、加签等操作

    首先附上OSS基本介绍和官方文档链接:https://help.aliyun.com/product/31815.html?spm=ata.21736010.0.0.25d67536bR4cly 另外 ...

  7. 08_让koa支持文件上传

    在koaBody中开启: const koa = require('koa') const router = require('../router') const koaBody = require( ...

  8. Easy.Ajax 部分源代码, 支持文件上传功能, 兼容所有主流浏览器

    下面是Easy.Ajax类的初稿,如须发表,在代码上还要修改以达到最简,但API是不会变了, Easy.Ajax = (function (WINDOW) { ajax = { proxyPool: ...

  9. ckfinder java 源码_Ckeditor与Ckfinder(java)整合实现富媒体内容编辑(支持文件上传)

    先来看一下最终的效果图 一.编辑器界面 二.上传图片界面 一.安装包下载,我使用的安装包是ckfinder_java_2.3.zip和ckeditor_3.6.3.zip,这两个文件可以分别到 htt ...

最新文章

  1. C语言关键字、标识符和注释
  2. stm32影子寄存器、预装载寄存器,TIM_OC1PreloadConfig和TIM_ARRPreloadConfig的作用
  3. jQuery选择器和选取方法 http://www.cnblogs.com/MaxIE/p/4078869.html
  4. 两个单链表相交的一系列问题
  5. 【Paddle】Anaconda安装PaddlePaddle和PaddleX教程
  6. linux环境下安装gcc
  7. 模拟模型学习 几何布朗运动_Java的几何布朗运动
  8. postman使用指南
  9. MyCat分布式数据库集群架构工作笔记0001---Mycat简介
  10. web测试 结果存储类型为“Database”,但尚未指定结果储存库连接字符串
  11. java随机抽取数字_java 获取随机数字的三种方法
  12. 计算机函数乘法word,【2人回答】Word里相乘的函数是什么?-3D溜溜网
  13. android studio 自定义皮肤
  14. 选计算机硬盘原则和注意事项,旧电脑升级是升内存还是SSD?按照这个原则就对了!...
  15. 大数据学习一般都需要学习哪些知识
  16. accumulate详细用法
  17. 国庆作业之感想与总结
  18. 微信直播的应用场景有哪些
  19. 彩信库 mmslib 设计备忘录
  20. redis哨兵集群部署 docker单机模拟-六节点

热门文章

  1. Mockplus设计大赛获奖选手专访 | High音:轻松生活,随心嗨音
  2. Spring Boot项目部署到Heroku
  3. 太晚睡不着的落寞与开心(记近况)
  4. java面试基础问题积累----多线程,并发
  5. ________________2058_______________后台数据不够。可能误判。
  6. Java基础—IO流
  7. SQLserver创建与主外键的看法
  8. Ubuntu 14.04使用命令行安装VirtualBox
  9. 动态代理机制之查看一个类或接口中有哪些方法
  10. asp.net中jQuery $post用法