Spiga

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

2007-04-04 12:16 by Jeffrey Zhao, 10387 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

Add your comment

57 条回复

  1. #1楼[楼主] Jeffrey Zhao      2007-04-04 12:21
    用中文说同样的内容果然比较省啊。
      回复  引用  查看    
  2. #2楼 哈密瓜牌牛奶      2007-04-04 14:15
    fighting~~!!好喜欢老赵与小赵这两个家伙,现在我也要开始Asp.net Ajax 的学习历程:)多谢,收下先
      回复  引用  查看    
  3. #3楼 jesh[未注册用户]2007-04-04 16:23
    赵老啊,用了你这个新的控件果然解决了那个 "500" 的错误,太感谢你了,这个控件很有用途,多向你学习不会错
      回复  引用    
  4. #4楼[楼主] Jeffrey Zhao      2007-04-04 17:02
    @jesh
    以前您看到的错误提示是假的,我的控件伪造的,呵呵。
    其实是因为解析错误才导致的问题,这次我在解析错误的问题上想的头皮都麻了……总算找到一个兼容各种主流浏览器的方法了。这个问题上IE6和IE7都不同,哎……
      回复  引用  查看    
  5. #5楼 11[匿名][未注册用户]2007-04-04 20:11
    呀,,正在做上传,,拿来用一下,不见怪撒..
      回复  引用    
  6. #6楼 jesh[未注册用户]2007-04-04 21:15
    :)
      回复  引用    
  7. #7楼[楼主] Jeffrey Zhao      2007-04-04 23:05
    @11[匿名]
    没事。:)
      回复  引用  查看    
  8. #8楼 MK2      2007-04-05 02:58
    哈哈,竟然要先看英文的。。。。。。以后希望中文优先。。
      回复  引用  查看    
  9. #9楼[楼主] Jeffrey Zhao      2007-04-05 11:58
    @MK2
    :)
      回复  引用  查看    
  10. #10楼 Fish[未注册用户]2007-05-13 12:24
    您好,我使用你的这个控件后,上传文件是没问题了,但是我加了判断文件大小的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. #11楼[楼主] Jeffrey Zhao      2007-05-13 22:46
    @Fish
    能不能给我看一下完整的例子呢?越简单越好,只要能够反映问题。:)
      回复  引用  查看    
  12. #12楼 Fish[未注册用户]2007-05-14 08:41
    再加个服务器的方法:

    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. #13楼 Fish[未注册用户]2007-05-14 08:42
    感谢您的回复! :)
      回复  引用    
  14. #14楼 Fish[未注册用户]2007-05-14 08:53
    <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. #15楼 Fish[未注册用户]2007-05-14 10:10
    上面那个sys的错误是在在个新建的网站出现的问题..
    但是我在我现在的项目里,新建个页面,只加了上面这些控件和方法.
    还是先那个错误,上传是成功了,报500的错误,再说43行注释未结束..
    我也不知道为什么哦,,麻烦您看看..
      回复  引用    
  16. #16楼 Fish[未注册用户]2007-05-14 11:51
    昏迷了,后来实验了才知道.您的那控件必须放在UpdatePanel里..
    如果只放在ScriptManager下面,保存是保存了,但是会报500的错误.
    谢谢您了,解决了ing!
      回复  引用    
  17. #17楼[楼主] Jeffrey Zhao      2007-05-14 12:01
    @Fish
    不是啊,我的设计是一定要紧贴着ScriptManager放的。您的问题可以给我看一下吗?
      回复  引用  查看    
  18. #18楼[楼主] Jeffrey Zhao      2007-05-14 12:03
    @Jeffrey Zhao
    请发到jeffz[at]live.com吧。:)
      回复  引用  查看    
  19. #19楼 Fish[未注册用户]2007-05-16 16:07
    我的问题已经说明白了嘛.就是500的错误.
    图片还是上传了.传完后报的错.
    我的代码先已经发上来了啦.
    比如你可以copy着运行试下,大概就是这样,如果您的没问题.
    我再写个简单的错误Demo给您吧!
      回复  引用    
  20. #20楼 Fish[未注册用户]2007-05-16 16:08
    jeffz[at]live.com
    这是什么地址啊?
      回复  引用    
  21. #21楼[楼主] Jeffrey Zhao      2007-05-16 19:52
    @Fish
    把[at]换成@就可以了。
      回复  引用  查看    
  22. #22楼 Fish[未注册用户]2007-05-17 11:06
    我晚上传个Demo你吧,现在工作中又遇到一个问题.
    使用UpdatePanel后,比如我写了window的load事件执行某一方法.
    一开始加载是执行了,点击分页的下一页后它就不执行了,
    我现在的解决办法是,setTimeout,不段执行相应的方法.
    能有好的解决方法么?.谢谢!...
    因为我是调用js的 table 点击表头排序.显示第1页数据时成功调用,没问题,
    显示第2页后它就不调用了!...谢谢解答.!
      回复  引用    
  23. #23楼 Fish[未注册用户]2007-05-17 12:41
    趁中午吃饭时间写了个上传图片的问题例子你了哦。注意查收!
    标题:
    Fish->图片上传问题例子

    :)
      回复  引用    
  24. #24楼[楼主] Jeffrey Zhao      2007-05-17 19:47
    @Fish
    不能用window.load,用Sys.Application.add_load(...)。
      回复  引用  查看    
  25. #25楼 Fish[未注册用户]2007-05-18 09:44
    谢谢老赵,有没有Update的技巧专贴啊,我看您的帖子,除了可以用您的控件外,那些代码看都看不懂,主要是sys这对象不熟悉,能否写个全面的Update使用技巧的帖子啊,嘿嘿...或者sys的也行!.谢谢啦.,,这样我也好学习,看您帖子里的代码看得头还是有点晕的.
      回复  引用    
  26. #26楼[楼主] Jeffrey Zhao      2007-05-18 13:04
    @Fish
    我还是建议看官方文档吧,我的文章都是基于基础使用来的扩展。:)
      回复  引用  查看    
  27. #27楼 爱情守望者      2007-05-25 14:48
    首先要多谢老赵多次帮助我解决一些问题!
    我还想跟老赵同志提一点点意见
    可不可以在提供代码的时候多一些注释,尽量让我这个低手能够更好的理解
    谢谢啊,这点上我非常欣赏日本的开发方式,严格的要求
    好像发错地方了,希望老赵会看到,呵呵
      回复  引用  查看    
  28. #28楼[楼主] Jeffrey Zhao      2007-05-28 15:26
    @爱情守望者
    好的代码是不用注释就能看懂的,我想我的代码应该做到了这一点吧。
    多余的注释反而是bad smell,日本外包这种做法我觉得并不可取。:)
      回复  引用  查看    
  29. #29楼 reddatura[未注册用户]2007-09-21 09:00
    我也出现了500 错误, 现在有解决方法了吗
      回复  引用    
  30. #30楼[楼主] Jeffrey Zhao      2007-09-21 09:20
    @reddatura
    500错误是假的,而是因为返回了客户端无法解析的内容,具体的原因就千奇百怪了。
      回复  引用  查看    
  31. #31楼 玄鐵劍[未注册用户]2007-09-25 16:53
    採用MasterPage,在MasterPage使用ScriptManager,繼承MasterPage後再新的頁面,無論如何都不上傳,這是啥原因噢?
      回复  引用    
  32. #32楼[楼主] Jeffrey Zhao      2007-09-25 19:32
    @玄鐵劍
    Helper是紧跟着ScriptManager放置的吗?
      回复  引用  查看    
  33. #33楼 崽崽[未注册用户]2007-09-28 09:31
    是挨着放的,出现这个问题怎么解决!parsing near '<script type='text/j'
      回复  引用    
  34. #34楼[楼主] Jeffrey Zhao      2007-09-28 22:43
    @崽崽
    UpdatePanel里放置了<script></script>元素?我记得已经更新过了……
      回复  引用  查看    
  35. #35楼 崽崽[未注册用户]2007-09-29 16:58
    <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. #36楼 mengyexue[未注册用户]2008-01-16 18:12
    楼主有没有在firefox测试,我用你这个控件时候,在firefox状态栏一直显示“正在从localhost传送数据”,并且页面的鼠标一直处于运行状态,文件上传是成功的,只是用户体验不是很好,特别是鼠标的运行状态,不知道能不能改善一下!
      回复  引用    
  37. #37楼 无法前行[未注册用户]2008-02-25 16:28
    我也遇到了和37楼同样的问题,如何解决那
      回复  引用    
  38. #38楼 zzzz[未注册用户]2008-04-23 21:36
    此文章的精华,是不是使用iframe来上传?
      回复  引用    
  39. #39楼[楼主] Jeffrey Zhao      2008-04-23 22:46
    @zzzz
      回复  引用  查看    
  40. #40楼 欧阳11[未注册用户]2008-05-30 09:50
    下了个,但是页面还是刷新!不知道为什么
      回复  引用    
  41. #41楼 teny[未注册用户]2008-06-06 15:15
    支持3.5吗,好像只能支持1.0的
      回复  引用    
  42. #42楼[楼主] Jeffrey Zhao      2008-06-06 16:55
    @teny
    这是支持2.0的,看看最新的。
      回复  引用  查看    
  43. #43楼 宝宝爱宝宝[未注册用户]2008-06-16 10:41
    怎么还有刷新
      回复  引用    
  44. #44楼 小胡哥[未注册用户]2008-06-26 17:12
    刚试了下.
    页面上放个按钮 点击后 再去点UPDATEPANEL中的 上传 就出现找不到对象了!
    怎么解决啊!!!
      回复  引用    
  45. #45楼 蓝色海岸线      2008-08-01 19:21
    谢谢了,学到了不少!
      回复  引用  查看    
  46. #46楼 何叶明[未注册用户]2008-09-18 06:25
    如果在datalist编辑模板中使用上传组件,就不可以了,
      回复  引用    
  47. #47楼 未果,痛苦中[未注册用户]2008-11-12 10:14
    本地没有问题;
    部署到服务器上;点击上传按钮出现:the status code returned the server was:500.....
    未果,痛苦中
      回复  引用    
  48. #48楼 gavinsky_Feifei[未注册用户]2008-12-03 10:19
    现在的的还有刷新吧?
      回复  引用    
  49. #49楼 李天时8989[未注册用户]2008-12-09 10:15
    它的局限性在与一个updatepanel中只能放一个fileupload控件需要多个updatepanel配合使用,如果报错505那就是应用程序有错误,代码中抛出异常了。
      回复  引用    
  50. #50楼 韩雅琼[未注册用户]2008-12-16 11:05
    我也是刚学Ajax没多久,受益匪浅!谢谢各位大侠!
      回复  引用    
  51. #51楼 我也有这个问题[未注册用户]2009-01-08 10:27
    楼主有没有在firefox测试,我用你这个控件时候,在firefox状态栏一直显示“正在从localhost传送数据”,并且页面的鼠标一直处于运行状态,文件上传是成功的,只是用户体验不是很好,特别是鼠标的运行状态,不知道能不能改善一下!
      回复  引用    
  52. #52楼 我也有这个问题[未注册用户]2009-01-08 15:36
    firefox鼠标一直是沙漏状态,我对比了一下用了控件后多了一个ScriptResource.axd,这个是如何产生的?
      回复  引用    
  53. #53楼 az[未注册用户]2009-03-05 09:03
    老赵,顶你,非常感谢你的控件,解决了我的大麻烦。
      回复  引用    
  54. #54楼 DotJam[未注册用户]2009-04-18 21:38
    老赵,楼上有个没有解决的问题,我也碰到了,就是有masterpage的情况下,在content中添加你的控件,无法上传,请教?
      回复  引用    
  55. #55楼[楼主] Jeffrey Zhao      2009-04-18 22:54
    @DotJam
    这一系列文章只是个设想,不堪大用的。
      回复  引用  查看    



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 699553




相关文章:

相关链接: