Hello World
Spiga

让UpdatePanel支持文件上传(1):开始

2007-04-04 12:16 by 老赵, 13402 visits

UpdatePanel从一开始就无法支持AJAX的文件上传方式。Eilon Lipton写了一篇文章解释了这个问题的原因。文章中提供了两个绕开此问题的方法:

  1. 将“上传”按钮设为一个传统的PostBack控件而不是异步PostBack。您可以使用多种方法来这么做:例如将一个按钮放置在UpdatePanel外,将按钮设为某个UpdatePanel的PostBackTrigger,或者调用ScriptManager.RegisterPostBackControl来注册它。
  2. 建立一个不使用ASP.NET AJAX的上传页面,很多站点已经这么做了。

不过,我们为什么不使UpdatePanel兼容FileUpload控件(<input type="file" />)呢?如果可以这样,一定能够受需要使用UpdatePanel上传文件的用户欢迎。

我们首先要解决的问题是,找到一种能够将信息发送到服务器端的方法。我们都知道XMLHttpRequest只能发送字符串。在这里,我们使用和其他的异步上传文件的解决方案一样,使用iframe来上传文件。iframe元素是一个非常有用的东西,即使在AJAX这个概念出现之前,它已经被用于制作一些异步更新的效果了。

其次,我们应该如何改变当前传输数据的行为呢?幸亏Microsoft AJAX Library有着强大的异步通讯层,我们可以方便创建一个UpdatePanelIFrameExetender来继承Sys.Net.WebRequestExecutor,并且将它交给一个上传文件的WebRequest对象。因此,下面的代码可以作为我们开发组件的第一步:

Type.registerNamespace("Jeffz.Web");

// the new executor will use the element witch initiated the async postback.
Jeffz.Web.UpdatePanelIFrameExecutor = function(sourceElement)
{
    // ...
}
Jeffz.Web.UpdatePanelIFrameExecutor.prototype =
{
    // ...
}
Jeffz.Web.UpdatePanelIFrameExecutor.registerClass(
    "Jeffz.Web.UpdatePanelExecutor", Sys.Net.WebRequestExecutor);

Jeffz.Web.UpdatePanelIFrameExecutor._beginRequestHandler = function(sender, e)
{
    var inputList = document.getElementsByTagName("input");
    for (var i = 0; i < inputList.length; i++)
    {
        var type = inputList[i].type;
        if (type && type.toUpperCase() == "FILE")
        {
            e.get_request().set_executor(
	        new Jeffz.Web.UpdatePanelExecutor(e.get_postBackElement()));
            return;
        }
    }
}

Sys.Application.add_init(function()
{
    Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(
        Jeffz.Web.UpdatePanelIFrameExecutor._beginRequestHandler);
});

 

在上面的代码段中,我们在页面初始化时监听了PageRequestManager对象的beginRequest事件。当PageRequestManager触发了一个异步请求时,我们会检查页面上是否有<input type="file" />控件。如果存在的话,则创建一个新的UpdatePanelIFrameExecutor实例,并分配给即将执行的WebRequest对象。

根据异步通讯层的实现,WebRequest的作用只是一个保存请求信息的容器,至于如何向服务器端发送信息则完全是Executor的事情了。事实上Executor完全可以不理会WebRequest携带的信息自行处理,而我们的UpdatePanelIFrameExecutor就是这样的玩意儿。它会改变页面上的内容,将信息Post到IFrame元素中,并且处理从服务器端获得的数据。(未完待续)

 

点击这里下载整个项目

English Version

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

55 条回复

  1. 老赵
    admin
    链接

    老赵 2007-04-04 12:21:00

    用中文说同样的内容果然比较省啊。

  2. 哈密瓜牌牛奶
    *.*.*.*
    链接

    哈密瓜牌牛奶 2007-04-04 14:15:00

    fighting~~!!好喜欢老赵与小赵这两个家伙,现在我也要开始Asp.net Ajax 的学习历程:)多谢,收下先

  3. jesh[未注册用户]
    *.*.*.*
    链接

    jesh[未注册用户] 2007-04-04 16:23:00

    赵老啊,用了你这个新的控件果然解决了那个 "500" 的错误,太感谢你了,这个控件很有用途,多向你学习不会错

  4. 老赵
    admin
    链接

    老赵 2007-04-04 17:02:00

    @jesh
    以前您看到的错误提示是假的,我的控件伪造的,呵呵。
    其实是因为解析错误才导致的问题,这次我在解析错误的问题上想的头皮都麻了……总算找到一个兼容各种主流浏览器的方法了。这个问题上IE6和IE7都不同,哎……

  5. 11[匿名][未注册用户]
    *.*.*.*
    链接

    11[匿名][未注册用户] 2007-04-04 20:11:00

    呀,,正在做上传,,拿来用一下,不见怪撒..

  6. jesh[未注册用户]
    *.*.*.*
    链接

    jesh[未注册用户] 2007-04-04 21:15:00

    :)

  7. 老赵
    admin
    链接

    老赵 2007-04-04 23:05:00

    @11[匿名]
    没事。:)

  8. MK2
    *.*.*.*
    链接

    MK2 2007-04-05 02:58:00

    哈哈,竟然要先看英文的。。。。。。以后希望中文优先。。

  9. 老赵
    admin
    链接

    老赵 2007-04-05 11:58:00

    @MK2
    :)

  10. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-13 12:24:00

    您好,我使用你的这个控件后,上传文件是没问题了,但是我加了判断文件大小的JS后,判断什么的是对的,但是点击保存后,文件是上传了,但是上传完后它报500的错误.再看左下角页面的错误,显示112行注释未结束.我再看页面源码啊,112行完全没问题.麻烦您帮我看看吧.谢谢!

    <input id="UploadFile" runat="server" onchange="changeSrc(this)" name="fileUp" type="file" /><asp:Button ID="Button1" runat="server" OnClick="Button1_Click" OnClientClick="return CanUpLoad();" Text="上传"/>
    <asp:Label ID="LabImage" runat="server"></asp:Label> <img src="about:blank" id="fileChecker" alt="商品图片" style="display:none;"/>


    以下为JS:放在这些控件下面的.

    <script type="text/javascript">
    var oFileChecker = document.getElementById("fileChecker");
    var Upload=false;

    function changeSrc(filePicker)
    {
    oFileChecker.src = filePicker.value;
    oFileChecker.style.display="";
    }

    oFileChecker.onreadystatechange = function ()
    {
    if (oFileChecker.readyState == "complete")
    {
    document.getElementById('LabImage').innerText="";
    checkSize();
    }
    }

    function checkSize()
    {
    if (oFileChecker.fileSize > 15360)
    {
    document.getElementById('LabImage').innerText="文件过大,请重新选择!";
    Upload=false;
    }
    else
    {
    Upload=true;
    }
    }

    function CanUpLoad()
    {
    return Upload;
    }
    </script>

  11. 老赵
    admin
    链接

    老赵 2007-05-13 22:46:00

    @Fish
    能不能给我看一下完整的例子呢?越简单越好,只要能够反映问题。:)

  12. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-14 08:41:00

    再加个服务器的方法:

    protected void Button1_Click(object sender, EventArgs e)
    {
    if (UploadFile.PostedFile.FileName.Trim() != "")
    {
    //上传文件
    //得到后缀名
    string extension = Path.GetExtension(UploadFile.PostedFile.FileName).ToLower();
    //验证是图片的方法
    if (Common.ValidateUtil.isPicture(extension))
    {
    //根据时间创建文件名
    string fileName = DateTime.Now.ToString("yyyyMMddhhmmss");]
    //工程路径+upfile文件夹+加文件名和后缀
    string path = Request.PhysicalApplicationPath + "/upfile/" + fileName + extension;
    //上传
    UploadFile.PostedFile.SaveAs(path);
    LabImage.Text = "上传成功!";
    //设置上传按钮不可用
    Button1.Enabled = false;
    }
    else
    {
    LabImage.Text = "只能上传图片!";
    }
    }
    }

    下面这就是控件:

    <input id="UploadFile" runat="server" onchange="changeSrc(this)" name="fileUp" type="file" /><asp:Button ID="Button1" runat="server" OnClick="Button1_Click" OnClientClick="return CanUpLoad();" Text="上传"/>
    <asp:Label ID="LabImage" runat="server"></asp:Label> <img src="about:blank" id="fileChecker" alt="商品图片" style="display:none;"/>


    <script type="text/javascript">
    var oFileChecker = document.getElementById("fileChecker");
    var Upload=false;

    function changeSrc(filePicker)
    {
    oFileChecker.src = filePicker.value;
    oFileChecker.style.display="";
    }

    oFileChecker.onreadystatechange = function ()
    {
    if (oFileChecker.readyState == "complete")
    {
    document.getElementById('LabImage').innerText="";
    checkSize();
    }
    }

    function checkSize()
    {
    if (oFileChecker.fileSize > 15360)
    {
    document.getElementById('LabImage').innerText="文件过大,请重新选择!";
    Upload=false;
    }
    else
    {
    Upload=true;
    }
    }

    function CanUpLoad()
    {
    return Upload;
    }
    </script>

    这个就是javascript 方法:

    <script type="text/javascript">
    var oFileChecker = document.getElementById("fileChecker");
    var Upload=false;

    function changeSrc(filePicker)
    {
    oFileChecker.src = filePicker.value;
    oFileChecker.style.display="";
    }

    oFileChecker.onreadystatechange = function ()
    {
    if (oFileChecker.readyState == "complete")
    {
    document.getElementById('LabImage').innerText="";
    checkSize();
    }
    }

    function checkSize()
    {
    if (oFileChecker.fileSize > 15360)
    {
    document.getElementById('LabImage').innerText="文件过大,请重新选择!";
    Upload=false;
    }
    else
    {
    Upload=true;
    }
    }

    function CanUpLoad()
    {
    return Upload;
    }
    </script>

    也就这些,错误的意思就是,点击File控件的浏览选取图片,img控件显示图片,再根据fileSize属性得到文件大小,如果不合要求,上传按钮的客户端点击事件返回的就是false,如果图片大小正确,上传按钮就可以点击.
    我写的这些功能是实现了,但是图片上传完后它就报500的错误,紧接着看左下角IE错误提示后就提示显示112行注释未结束,当然我页面还有些其他东西,都是些无关紧要的东西.!我看112行也没啥子错误.显示的是其他的控件内容!麻烦您试下吧,谢谢!

  13. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-14 08:42:00

    感谢您的回复! :)

  14. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-14 08:53:00

    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <cc1:ajaxfileuploadhelper id="AjaxFileUploadHelper1" runat="server"></cc1:ajaxfileuploadhelper>
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
    <input id="UploadFile" runat="server" name="fileUp" onchange="changeSrc(this)" style="z-index: 102;
    left: 101px; width: 181px; top: 290px" type="file" /><asp:Button ID="Button1" runat="server"
    CssClass="Buttons" OnClick="Button1_Click" OnClientClick="return CanUpLoad();"
    Style="z-index: 102; left: 101px; top: 290px" Text="上传" /><asp:Label ID="LabImage"
    runat="server" ForeColor="Purple"></asp:Label><img id="fileChecker" alt="商品图片" src="about:blank"
    style="display: none" />
    <script type="text/javascript">
    var oFileChecker = document.getElementById("fileChecker");
    var Upload=false;

    function changeSrc(filePicker)
    {
    oFileChecker.src = filePicker.value;
    oFileChecker.style.display="";
    }

    oFileChecker.onreadystatechange = function ()
    {
    if (oFileChecker.readyState == "complete")
    {
    document.getElementById('LabImage').innerText="";
    checkSize();
    }
    }

    function checkSize()
    {
    if (oFileChecker.fileSize > 15360)
    {
    document.getElementById('LabImage').innerText="文件过大,请重新选择!";
    Upload=false;
    }
    else
    {
    Upload=true;
    }
    }

    function CanUpLoad()
    {
    return Upload;
    }
    </script>

    </ContentTemplate>
    </asp:UpdatePanel></div>
    </form>

    --------------------------------------------------------

    protected void Button1_Click(object sender, EventArgs e)
    {
    if (UploadFile.PostedFile.FileName.Trim() != "")
    {
    //上传文件
    string extension = Path.GetExtension(UploadFile.PostedFile.FileName).ToLower();
    string fileName = DateTime.Now.ToString("yyyyMMddhhmmss");
    string path = Request.PhysicalApplicationPath + "/upfile/" + fileName + extension;
    UploadFile.PostedFile.SaveAs(path);
    LabImage.Text = "上传成功!";
    LabImage.CssClass = fileName + extension;
    Button1.Enabled = false;

    }
    }
    晕,我把这些写在单独的个空页面里,它报sys未定义.又不是报先那个错了.
    也许是其他位置有问题?实在看不出来了.哎....

  15. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-14 10:10:00

    上面那个sys的错误是在在个新建的网站出现的问题..
    但是我在我现在的项目里,新建个页面,只加了上面这些控件和方法.
    还是先那个错误,上传是成功了,报500的错误,再说43行注释未结束..
    我也不知道为什么哦,,麻烦您看看..

  16. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-14 11:51:00

    昏迷了,后来实验了才知道.您的那控件必须放在UpdatePanel里..
    如果只放在ScriptManager下面,保存是保存了,但是会报500的错误.
    谢谢您了,解决了ing!

  17. 老赵
    admin
    链接

    老赵 2007-05-14 12:01:00

    @Fish
    不是啊,我的设计是一定要紧贴着ScriptManager放的。您的问题可以给我看一下吗?

  18. 老赵
    admin
    链接

    老赵 2007-05-14 12:03:00

    @Jeffrey Zhao
    请发到jeffz[at]live.com吧。:)

  19. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-16 16:07:00

    我的问题已经说明白了嘛.就是500的错误.
    图片还是上传了.传完后报的错.
    我的代码先已经发上来了啦.
    比如你可以copy着运行试下,大概就是这样,如果您的没问题.
    我再写个简单的错误Demo给您吧!

  20. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-16 16:08:00

    jeffz[at]live.com
    这是什么地址啊?

  21. 老赵
    admin
    链接

    老赵 2007-05-16 19:52:00

    @Fish
    把[at]换成@就可以了。

  22. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-17 11:06:00

    我晚上传个Demo你吧,现在工作中又遇到一个问题.
    使用UpdatePanel后,比如我写了window的load事件执行某一方法.
    一开始加载是执行了,点击分页的下一页后它就不执行了,
    我现在的解决办法是,setTimeout,不段执行相应的方法.
    能有好的解决方法么?.谢谢!...
    因为我是调用js的 table 点击表头排序.显示第1页数据时成功调用,没问题,
    显示第2页后它就不调用了!...谢谢解答.!

  23. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-17 12:41:00

    趁中午吃饭时间写了个上传图片的问题例子你了哦。注意查收!
    标题:
    Fish->图片上传问题例子

    :)

  24. 老赵
    admin
    链接

    老赵 2007-05-17 19:47:00

    @Fish
    不能用window.load,用Sys.Application.add_load(...)。

  25. Fish[未注册用户]
    *.*.*.*
    链接

    Fish[未注册用户] 2007-05-18 09:44:00

    谢谢老赵,有没有Update的技巧专贴啊,我看您的帖子,除了可以用您的控件外,那些代码看都看不懂,主要是sys这对象不熟悉,能否写个全面的Update使用技巧的帖子啊,嘿嘿...或者sys的也行!.谢谢啦.,,这样我也好学习,看您帖子里的代码看得头还是有点晕的.

  26. 老赵
    admin
    链接

    老赵 2007-05-18 13:04:00

    @Fish
    我还是建议看官方文档吧,我的文章都是基于基础使用来的扩展。:)

  27. 爱情守望者
    *.*.*.*
    链接

    爱情守望者 2007-05-25 14:48:00

    首先要多谢老赵多次帮助我解决一些问题!
    我还想跟老赵同志提一点点意见
    可不可以在提供代码的时候多一些注释,尽量让我这个低手能够更好的理解
    谢谢啊,这点上我非常欣赏日本的开发方式,严格的要求
    好像发错地方了,希望老赵会看到,呵呵

  28. 老赵
    admin
    链接

    老赵 2007-05-28 15:26:00

    @爱情守望者
    好的代码是不用注释就能看懂的,我想我的代码应该做到了这一点吧。
    多余的注释反而是bad smell,日本外包这种做法我觉得并不可取。:)

  29. reddatura[未注册用户]
    *.*.*.*
    链接

    reddatura[未注册用户] 2007-09-21 09:00:00

    我也出现了500 错误, 现在有解决方法了吗

  30. 老赵
    admin
    链接

    老赵 2007-09-21 09:20:00

    @reddatura
    500错误是假的,而是因为返回了客户端无法解析的内容,具体的原因就千奇百怪了。

  31. 玄鐵劍[未注册用户]
    *.*.*.*
    链接

    玄鐵劍[未注册用户] 2007-09-25 16:53:00

    採用MasterPage,在MasterPage使用ScriptManager,繼承MasterPage後再新的頁面,無論如何都不上傳,這是啥原因噢?

  32. 老赵
    admin
    链接

    老赵 2007-09-25 19:32:00

    @玄鐵劍
    Helper是紧跟着ScriptManager放置的吗?

  33. 崽崽[未注册用户]
    *.*.*.*
    链接

    崽崽[未注册用户] 2007-09-28 09:31:00

    是挨着放的,出现这个问题怎么解决!parsing near '<script type='text/j'

  34. 老赵
    admin
    链接

    老赵 2007-09-28 22:43:00

    @崽崽
    UpdatePanel里放置了<script></script>元素?我记得已经更新过了……

  35. 崽崽[未注册用户]
    *.*.*.*
    链接

    崽崽[未注册用户] 2007-09-29 16:58:00

    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <jeffz:AjaxFileUploadHelper ID="AjaxFileUploadHelper1" runat="server" />
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
    <asp:FileUpload ID="FileUpload1" runat="server" />
    为什么要挨着<asp:UpdatePanel ID="UpdatePanel1" runat="server">
    这个放,而且吧FileUpload1设置为不显示又会出parsing near '<script type='text/j'这个错,我是想点一个按钮然后再出现那个添加信息的面板,这样怎么做到,学习了,谢谢!!

  36. mengyexue[未注册用户]
    *.*.*.*
    链接

    mengyexue[未注册用户] 2008-01-16 18:12:00

    楼主有没有在firefox测试,我用你这个控件时候,在firefox状态栏一直显示“正在从localhost传送数据”,并且页面的鼠标一直处于运行状态,文件上传是成功的,只是用户体验不是很好,特别是鼠标的运行状态,不知道能不能改善一下!

  37. 无法前行[未注册用户]
    *.*.*.*
    链接

    无法前行[未注册用户] 2008-02-25 16:28:00

    我也遇到了和37楼同样的问题,如何解决那

  38. zzzz[未注册用户]
    *.*.*.*
    链接

    zzzz[未注册用户] 2008-04-23 21:36:00

    此文章的精华,是不是使用iframe来上传?

  39. 老赵
    admin
    链接

    老赵 2008-04-23 22:46:00

    @zzzz

  40. 欧阳11[未注册用户]
    *.*.*.*
    链接

    欧阳11[未注册用户] 2008-05-30 09:50:00

    下了个,但是页面还是刷新!不知道为什么

  41. teny[未注册用户]
    *.*.*.*
    链接

    teny[未注册用户] 2008-06-06 15:15:00

    支持3.5吗,好像只能支持1.0的

  42. 老赵
    admin
    链接

    老赵 2008-06-06 16:55:00

    @teny
    这是支持2.0的,看看最新的。

  43. 宝宝爱宝宝[未注册用户]
    *.*.*.*
    链接

    宝宝爱宝宝[未注册用户] 2008-06-16 10:41:00

    怎么还有刷新

  44. 小胡哥[未注册用户]
    *.*.*.*
    链接

    小胡哥[未注册用户] 2008-06-26 17:12:00

    刚试了下.
    页面上放个按钮 点击后 再去点UPDATEPANEL中的 上传 就出现找不到对象了!
    怎么解决啊!!!

  45. 蓝色海岸线
    *.*.*.*
    链接

    蓝色海岸线 2008-08-01 19:21:00

    谢谢了,学到了不少!

  46. 何叶明[未注册用户]
    *.*.*.*
    链接

    何叶明[未注册用户] 2008-09-18 06:25:00

    如果在datalist编辑模板中使用上传组件,就不可以了,

  47. 未果,痛苦中[未注册用户]
    *.*.*.*
    链接

    未果,痛苦中[未注册用户] 2008-11-12 10:14:00

    本地没有问题;
    部署到服务器上;点击上传按钮出现:the status code returned the server was:500.....
    未果,痛苦中

  48. gavinsky_Feifei[未注册用户…
    *.*.*.*
    链接

    gavinsky_Feifei[未注册用户] 2008-12-03 10:19:00

    现在的的还有刷新吧?

  49. 李天时8989[未注册用户]
    *.*.*.*
    链接

    李天时8989[未注册用户] 2008-12-09 10:15:00

    它的局限性在与一个updatepanel中只能放一个fileupload控件需要多个updatepanel配合使用,如果报错505那就是应用程序有错误,代码中抛出异常了。

  50. 韩雅琼[未注册用户]
    *.*.*.*
    链接

    韩雅琼[未注册用户] 2008-12-16 11:05:00

    我也是刚学Ajax没多久,受益匪浅!谢谢各位大侠!

  51. 我也有这个问题[未注册用户]
    *.*.*.*
    链接

    我也有这个问题[未注册用户] 2009-01-08 10:27:00

    楼主有没有在firefox测试,我用你这个控件时候,在firefox状态栏一直显示“正在从localhost传送数据”,并且页面的鼠标一直处于运行状态,文件上传是成功的,只是用户体验不是很好,特别是鼠标的运行状态,不知道能不能改善一下!

  52. 我也有这个问题[未注册用户]
    *.*.*.*
    链接

    我也有这个问题[未注册用户] 2009-01-08 15:36:00

    firefox鼠标一直是沙漏状态,我对比了一下用了控件后多了一个ScriptResource.axd,这个是如何产生的?

  53. az[未注册用户]
    *.*.*.*
    链接

    az[未注册用户] 2009-03-05 09:03:00

    老赵,顶你,非常感谢你的控件,解决了我的大麻烦。

  54. DotJam[未注册用户]
    *.*.*.*
    链接

    DotJam[未注册用户] 2009-04-18 21:38:00

    老赵,楼上有个没有解决的问题,我也碰到了,就是有masterpage的情况下,在content中添加你的控件,无法上传,请教?

  55. 老赵
    admin
    链接

    老赵 2009-04-18 22:54:00

    @DotJam
    这一系列文章只是个设想,不堪大用的。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我