老赵点滴


  先做人,再做技术人员,最后做程序员。
  我的理想:“让外国人看中国人写的技术书籍和文章”。Try as I might
posts - 287, comments - 10549, trackbacks - 137, articles - 6
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

  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

Feedback

#1楼 [楼主]   回复  引用  查看    

2007-04-04 12:21 by Jeffrey Zhao      
用中文说同样的内容果然比较省啊。

#2楼    回复  引用  查看    

2007-04-04 14:15 by 哈密瓜牌牛奶      
fighting~~!!好喜欢老赵与小赵这两个家伙,现在我也要开始Asp.net Ajax 的学习历程:)多谢,收下先

#3楼    回复  引用    

2007-04-04 16:23 by jesh [未注册用户]
赵老啊,用了你这个新的控件果然解决了那个 "500" 的错误,太感谢你了,这个控件很有用途,多向你学习不会错

#4楼 [楼主]   回复  引用  查看    

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

#5楼    回复  引用    

2007-04-04 20:11 by 11[匿名] [未注册用户]
呀,,正在做上传,,拿来用一下,不见怪撒..

#6楼    回复  引用    

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

#7楼 [楼主]   回复  引用  查看    

2007-04-04 23:05 by Jeffrey Zhao      
@11[匿名]
没事。:)

#8楼    回复  引用  查看    

2007-04-05 02:58 by MK2      
哈哈,竟然要先看英文的。。。。。。以后希望中文优先。。

#9楼 [楼主]   回复  引用  查看    

2007-04-05 11:58 by Jeffrey Zhao      
@MK2
:)

#10楼    回复  引用    

2007-05-13 12:24 by Fish [未注册用户]
您好,我使用你的这个控件后,上传文件是没问题了,但是我加了判断文件大小的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楼 [楼主]   回复  引用  查看    

2007-05-13 22:46 by Jeffrey Zhao      
@Fish
能不能给我看一下完整的例子呢?越简单越好,只要能够反映问题。:)

#12楼    回复  引用    

2007-05-14 08:41 by Fish [未注册用户]
再加个服务器的方法:

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楼    回复  引用    

2007-05-14 08:42 by Fish [未注册用户]
感谢您的回复! :)

#14楼    回复  引用    

2007-05-14 08:53 by Fish [未注册用户]
<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楼    回复  引用    

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

#16楼    回复  引用    

2007-05-14 11:51 by Fish [未注册用户]
昏迷了,后来实验了才知道.您的那控件必须放在UpdatePanel里..
如果只放在ScriptManager下面,保存是保存了,但是会报500的错误.
谢谢您了,解决了ing!

#17楼 [楼主]   回复  引用  查看    

2007-05-14 12:01 by Jeffrey Zhao      
@Fish
不是啊,我的设计是一定要紧贴着ScriptManager放的。您的问题可以给我看一下吗?

#18楼 [楼主]   回复  引用  查看    

2007-05-14 12:03 by Jeffrey Zhao      
@Jeffrey Zhao
请发到jeffz[at]live.com吧。:)

#19楼    回复  引用    

2007-05-16 16:07 by Fish [未注册用户]
我的问题已经说明白了嘛.就是500的错误.
图片还是上传了.传完后报的错.
我的代码先已经发上来了啦.
比如你可以copy着运行试下,大概就是这样,如果您的没问题.
我再写个简单的错误Demo给您吧!

#20楼    回复  引用    

2007-05-16 16:08 by Fish [未注册用户]
jeffz[at]live.com
这是什么地址啊?

#21楼 [楼主]   回复  引用  查看    

2007-05-16 19:52 by Jeffrey Zhao      
@Fish
把[at]换成@就可以了。

#22楼    回复  引用    

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

#23楼    回复  引用    

2007-05-17 12:41 by Fish [未注册用户]
趁中午吃饭时间写了个上传图片的问题例子你了哦。注意查收!
标题:
Fish->图片上传问题例子

:)

#24楼 [楼主]   回复  引用  查看    

2007-05-17 19:47 by Jeffrey Zhao      
@Fish
不能用window.load,用Sys.Application.add_load(...)。

#25楼    回复  引用    

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

#26楼 [楼主]   回复  引用  查看    

2007-05-18 13:04 by Jeffrey Zhao      
@Fish
我还是建议看官方文档吧,我的文章都是基于基础使用来的扩展。:)

#27楼    回复  引用  查看    

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

#28楼 [楼主]   回复  引用  查看    

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

#29楼    回复  引用    

2007-09-21 09:00 by reddatura [未注册用户]
我也出现了500 错误, 现在有解决方法了吗

#30楼 [楼主]   回复  引用  查看    

2007-09-21 09:20 by Jeffrey Zhao      
@reddatura
500错误是假的,而是因为返回了客户端无法解析的内容,具体的原因就千奇百怪了。

#31楼    回复  引用    

2007-09-25 16:53 by 玄鐵劍 [未注册用户]
採用MasterPage,在MasterPage使用ScriptManager,繼承MasterPage後再新的頁面,無論如何都不上傳,這是啥原因噢?

#32楼 [楼主]   回复  引用  查看    

2007-09-25 19:32 by Jeffrey Zhao      
@玄鐵劍
Helper是紧跟着ScriptManager放置的吗?

#33楼    回复  引用    

2007-09-28 09:31 by 崽崽 [未注册用户]
是挨着放的,出现这个问题怎么解决!parsing near '<script type='text/j'

#34楼 [楼主]   回复  引用  查看    

2007-09-28 22:43 by Jeffrey Zhao      
@崽崽
UpdatePanel里放置了<script></script>元素?我记得已经更新过了……

#35楼    回复  引用    

2007-09-29 16:58 by 崽崽 [未注册用户]
<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楼    回复  引用    

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

#37楼    回复  引用    

2008-02-25 16:28 by 无法前行 [未注册用户]
我也遇到了和37楼同样的问题,如何解决那

#38楼    回复  引用    

2008-04-23 21:36 by zzzz [未注册用户]
此文章的精华,是不是使用iframe来上传?

#39楼 [楼主]   回复  引用  查看    

2008-04-23 22:46 by Jeffrey Zhao      
@zzzz

#40楼    回复  引用    

2008-05-30 09:50 by 欧阳11 [未注册用户]
下了个,但是页面还是刷新!不知道为什么

#41楼    回复  引用    

2008-06-06 15:15 by teny [未注册用户]
支持3.5吗,好像只能支持1.0的

#42楼 [楼主]   回复  引用  查看    

2008-06-06 16:55 by Jeffrey Zhao      
@teny
这是支持2.0的,看看最新的。

#43楼    回复  引用    

2008-06-16 10:41 by 宝宝爱宝宝 [未注册用户]
怎么还有刷新

#44楼    回复  引用    

2008-06-26 17:12 by 小胡哥 [未注册用户]
刚试了下.
页面上放个按钮 点击后 再去点UPDATEPANEL中的 上传 就出现找不到对象了!
怎么解决啊!!!

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-04-15 07:51 编辑过
 
另存  打印