转自: http://www.cnblogs.com/cloudgamer/archive/2008/11/28/1314766.html?
本文仅供个人参考资料用,如果您搜索到此博文,请查看原文链接。
效果预览:
这里的预览只是前台的效果,要整个系统测试请下载完整实例。
程序说明
【无刷新上传】
要实现文件上传,form必须设置几个属性:
1.action:设为要处理数据的页面地址;
2.method:设为"post";
3.enctype/encoding:必须设为"multipart/form-data",这里要注意的是在ie中用js修改form的enctype属性是没有效果的,只能修改encoding;
后面两个属性程序初始化时都有设置:
this.Form.method = "post";
this.Form.encoding = "multipart/form-data";
要注意这里的无刷新不是ajax哦,而是利用“古老”的iframe。
由于ajax提交数据必须先获取数据,而js(一般情况下)是不能操作客户端文件,要获取文件数据就更不用说了,所以只能用iframe来做。
先说说iframe实现无刷新上传的原理:利用form的target属性,把数据提交到页面中一个(通常为隐藏的)iframe上。直观点说就是把“刷新”留给iframe。
其实原理跟一般用iframe实现无刷新提交表单是一样的,只是这里换成是文件。
这里关键就是把form的target设为iframe的name:
this.Form.target = this._FrameName;
【iframe】
如果没有自定义iframe,程序在初始化时会自动创建无刷新所需的iframe的。
首先必须选择一个iframe名,这在无刷新时是必须的,为了每个实例能创建各自的iframe,这里用了一个随机数:
this._FrameName = "uploadFrame_" + Math.floor(Math.random() * 1000);
也可以用一个递增的计算器来代替随机数。
接着创建iframe,本以为用document.createElement("iframe")创建再设置它的name属性就行了。
却发现这样设置的name在ie居然不认(有说name是只读属性),还好在网上找到一个方法:“IE 创建元素,还有一个特点,就是可以连同属性一同创建”。
例如我想给动态创建的iframe设置name,可以这样:
document.createElement("<iframe name=\"" + this._FrameName + "\">")
不过这个方式在ff会报错:
uncaught exception: String contains an invalid character (NS_ERROR_DOM_INVALID_CHARACTER_ERR)
估计是用createElement时不能带name,标准应该也是这样,所以兼容的方式这样写:
Code
var oFrame = isIE ? document.createElement("<iframe name=\"" + this._FrameName + "\">") : document.createElement("iframe");
//为ff设置name
oFrame.name = this._FrameName;
oFrame.style.display = "none";
关于这方面更详细的内容请看这里。
创建完还需要插入到body中,一般的做法是使用document.body.appendChild,但在ie中会有“已终止操作”错误,可以用下面这段代码测试:
Code
<body>
<div>
<script>
document.body.appendChild(document.createElement("div"));
</script>
</div>
</body>
网上找到一个解析:“原来FF下的实现机制是当页面还没有完全读取完时body元素就已经存在了,而IE只有页面完全读取结束body元素才会存在,所以在页面中插入上面这条语句在IE下就会出现错误”。
我在web开发未解之谜中也提到了这个现象,我这里使用了insertBefore代替:
document.body.insertBefore(oFrame, document.body.childNodes[0]);
在服务器端文件传送完(或失败)之后,怎么通知客户端呢?
这里说说我的方法,首先我在客户端定义一个函数:
function Finish(msg){ alert(msg); location.href = location.href; }
很简单,就是显示提示并重新加载页面(如果使用reload会导致ff中iframe重复加载数据)。
那服务器端如何通知客户端的问题,就是iframe如何跟主页面交互。
答案是通过window.parent或window.top,在iframe中parent和top属性“分别返回立即父窗口和最上层的祖先窗口”。
例如我在服务器端处理完数据之后会输出:
context.Response.Write("<script>window.parent.Finish('" + _msg + "');</script>");
就会执行主页面的Finish函数了。
【多文件上传】
对于多文件上传,这里的目的是如何做到163网盘那样,只用一个file控件就实现多文件上传。
这里参考了163网盘的思路,下面说说如何实现:
首先必须有一个文件空间(我自己定的名字),例如程序中的"idFile"对象,这个空间不需要内容甚至一个div就可以,主要是用来存放file控件,程序中Folder属性就是这个文件空间对象。
ps:这里的要求是把file控件都控制在文件空间里,即使不是单file控件的情况。
再说说Files属性,这个属性放的是file控件集合,方便获取file控件,在下面“文件列表”就会用到。
处理这些file控件的程序主要在Ini函数中:
首先是处理文件空间中的file控件:
Code
this.Files = [];
//整理文件空间,把有值的file放入文件集合
Each(this.Folder.getElementsByTagName("input"), Bind(this, function(o){
if(o.type == "file"){ o.value && this.Files.push(o); this.onIniFile(o); }
}))
可以看到这里主要是把file控件放入到Files中,并执行附加函数onIniFile,我是这样定义这个函数的:
onIniFile: function(file){ file.value ? file.style.display = "none" : this.Folder.removeChild(file); }
这里为了实现单file控件,把原来有值的file都隐藏了,还有那个“单file控件”呢?
别急,接着就在文件空间插入一个新的file控件:
Code
var file = document.createElement("input");
file.name = this.FileName; file.type = "file"; file.onchange = Bind(this, function(){ this.Check(file); this.Ini(); });
this.Folder.appendChild(file);
可以看到file控件的name是FileName属性的值,默认是空的,如果服务器端需要这个name的话就可以设置。
这里可以看到每个file控件都有onchange来执行检测函数Check,这样每次选择文件后都会用Check检测一次,这里说说这个Check函数:
Code
//检测变量
var bCheck = true;
//进行空值、文件数、后缀名、同值检测
if(!file.value){
bCheck = false; this.onEmpty();
} else if(this.Limit && this.Files.length >= this.Limit){
bCheck = false; this.onLimite();
} else if(!!this.ExtIn.length && !RegExp("\.(" + this.ExtIn.join("|") + ")$", "i").test(file.value)){
//检测是否允许后缀名
bCheck = false; this.onNotExtIn();
} else if(!!this.ExtOut.length && RegExp("\.(" + this.ExtOut.join("|") + ")$", "i").test(file.value)) {
//检测是否禁止后缀名
bCheck = false; this.onExtOut();
} else if(!!this.Distinct) {
Each(this.Files, function(o){ if(o.value == file.value){ bCheck = false; } })
if(!bCheck){ this.onSame(); }
}
里面有一个检测变量bCheck,然后进行空值、文件数限制、后缀名、相同文件的检测,当其中一个步骤不通过bCheck就会设为false,一个常用的检测结构。
这里说说检测后缀名,由于js不能像后台那样获取文件的文件类型,所以只能根据后缀名来判断,例如用正则判断:
/\.(jpg|gif)$/i.test(file.value)
这样判断显然是不够的,所以如果要做文件类型判断的话一定要在后台用ContentType再判断一次。
最后如果没有通过检测就会执行onFail函数:
!bCheck && this.onFail(file);
我在onFail函数中设定了移除没有通过检测的file控件:
onFail: function(file){ this.Folder.removeChild(file); }
这样就基本实现(正确的说是模拟)了单file控件上传多个文件的效果了。
【文件列表】
在上面的Ini函数中,最后执行了一个附加函数onIni,这个函数是用户自己定义的,我就在这个函数中添加文件列表。
在之前先说说添加文件列表的函数AddList,这个函数是用来把file控件的值列在一个table里面。
函数的参数是一个二维数组,其中第一维是行(tr),第二维是列(td)。
首先获取列表对象FileList,再定义一个文档碎片oFragment来操作dom:
var FileList = $("idFileList"), oFragment = document.createDocumentFragment();
然后用两个Each把二维数组插入到文档碎片中:
Code
Each(rows, function(cells){
var row = document.createElement("tr");
Each(cells, function(o){
var cell = document.createElement("td");
if(typeof o == "string"){ cell.innerHTML = o; }else{ cell.appendChild(o); }
row.appendChild(cell);
});
oFragment.appendChild(row);
})
其中用了一个判断if(typeof o == "string"),如果是文本就直接用innerHTML插入td,如果不是文本(这里不是文本就是一个对象)就用appendChild插入到td。
当数据都插入到文档碎片,就准备把文档碎片插入到FileList中,不过还有一个步骤就是清空FileList中原有的数据。
本来把innerHTML设为空来清空FileList会更有效率,但ie的table中只有td支持innerHTML,所以只好用removeChild来清空:
while(FileList.hasChildNodes()){ FileList.removeChild(FileList.firstChild); }
之后就可以把文档碎片插入了:
FileList.appendChild(oFragment);
继续看onIni函数,现在只需要把要显示的数据组成一个二维数组,再用AddList就能显示文件列表了,这时存放file控件集合的Files属性就大有用处了。
首先定义一个放显示数据的数组:
var arrRows = [];
然后根据Files对这个数组赋值:
Code
if(this.Files.length){
var oThis = this;
Each(this.Files, function(o){
var a = document.createElement("a"); a.innerHTML = "取消"; a.href = "javascript:void(0);";
a.onclick = function(){ oThis.Delete(o); return false; };
arrRows.push([o.value, a]);
});
} else { arrRows.push(["<font color='gray'>没有添加文件</font>", " "]); }
AddRow(arrRows);
当Files没有控件时只是输出“没有添加文件”,有控件时就会把每个file控件的要显示数据放到一个数组中,可以看到这个数组其实就是td内容的集合,接着把这个数组加入到arrRows中形成二维数组,最后把得到的arrRows给AddRow函数显示数据就行了。
为了能取消指定的file控件,这里插入了一个a来触发删除函数Delete,这里也有一个技巧,这里把href设为"javascript:void(0);",并在onclick中返回false,这样能最大程度的实现仅仅执行js而不去跳转。
在表单提交时也要重新显示文件列表,表单提交后就不允许删除文件了,只显示文件路径就行了:
Code
$("idBtnupload").onclick = function(){
//显示文件列表
var arrRows = [];
Each(fu.Files, function(o){ arrRows.push([o.value, " "]); });
AddList(arrRows);
fu.Folder.style.display = "none";
$("idProcess").style.display = "";
$("idMsg").innerHTML = "正在添加文件到您的网盘中,请稍候……<br />有可能因为网络问题,出现程序长时间无响应,请点击“<a href='?'><font color='red'>取消</font></a>”重新上传文件";
fu.Form.submit();
}
说到表单提交要注意一个问题,就是表单是不能嵌套的,最好是把表单放到服务器表单之外,没有办法才使用服务器表单作为提交表单(由于程序会修改提交表单的属性,所以尽量不要这样使用)。
这样文件列表就完成了,有兴趣的话也可以自己封装一下这个功能。
【file样式】
到此,程序的功能都已经实现了,但在163网盘中还有一个特别的地方,就是file控件的样式。
如果有用过163网盘上传文件,就知道那个file控件就像一个按钮,但功能确实是一个file控件。
但当自己尝试修改file控件的样式时,发现单单设置file控件的样式并不能实现想要的效果。
于是我想了另一个办法,用一个button来模拟,结果发现也不行,用js根本操作不了file控件,应该是考虑到安全问题吧。
最后是参考了163网盘和muxrwc模拟126附件添加的效果,总结了这个方法:
1.指定用一个容器(例如程序中的idFile)。
容器最好指定高和宽,并且overflow为hidden,不是块级元素的最好设display为block(为了高和宽的正确呈现);
2.在容器里放一个file控件,并设置样式,使能触发弹出选择文件框的部分覆盖整个容器,并设置成全透明。
容器指定准确的高和宽就是为了能通过file控件中不多的能设置的样式来覆盖整个容器;
3.现在已经把容器模拟成file控件了,可以直接设置容器的样式来模拟设置file控件的样式了。
在程序中主要用file控件的margin-left和font-size来实现覆盖整个容器:
Code
a.files input {
margin-left:-350px;
font-size:30px;
cursor:pointer;
filter:alpha(opacity=0);
opacity:0;
}
至于容器,我使用了有伪类hover的a元素(虽然CSS2中hover可以应用于任何对象,但ie6不支持)。
这里用了一个常用的小技巧,就是用一张图片作为背景通过在hover时修改background-position来实现两张图片的效果:
Code
a.files {
width:90px;
height:30px;
overflow:hidden;
display:block;
border:1px solid #BEBEBE;
background:url(img/fu_btn.gif) left top no-repeat;
text-decoration:none;
}
a.files:hover {
background-color:#FFFFEE;
background-position:0 -30px;
}
在点击这个a时后会出现一个虚线框,在这里显然不太美观,可以把outline设为none来去掉,可是ie又不支持,在网上找到一个方法ie可以把hideFocus设为true来隐藏聚焦(即不显示这个虚线框,hideFocus可以在js或html中设置,也可以通过expression放到css中:
Code
a.files, a.files input {
outline:none;/*ff* /
hide-focus:expression(this.hideFocus=true);/*ie* /
}
这样完全模拟了163网盘的效果了。
【后台】
前台基本完成了,就到后台啦。后台的功能很简单,就是处理传递过来的文件数据。
这里像js + .Net 图片切割系统那样使用ashx文件处理IHttpHandler发送过来的数据。
程序很简单,就直接贴代码了:
Code
int iTotal = context.Request.Files.Count;
if (iTotal == 0)
{
_msg = "没有数据";
}
else
{
int iCount = 0;
for (int i = 0; i < iTotal; i++)
{
HttpPostedFile file = context.Request.Files[i];
if (file.ContentLength > 0 || !string.IsNullOrEmpty(file.FileName))
{
//保存文件
file.SaveAs(System.Web.HttpContext.Current.Server.MapPath("./file/" + Path.GetFileName(file.FileName)));
//这里可以根据实际设置其他限制
if (++iCount > UploadFileLimit)
{
_msg = "超过上传限制:" + UploadFileLimit;
break;
}
}
}
}
这里只检测了有无文件和文件数限制,其他检测如文件大小等可以自己扩展,应该不难。
处理完数据之后就通知客户端:
context.Response.Write("<script>window.parent.Finish('" + _msg + "');</script>");
这个在上面iframe的内容中已经说明了。
使用说明
基本使用很简单,实例化一个file对象,其中参数分别是form对象,文件空间对象:
new FileUpload("uploadForm", "idFile")
这样就实现了一个简单的无刷新上传文件表单。
还可以使用这几个属性:
Form//表单
Folder//文件控件存放空间
Files//文件集合
更多的功能可以选择设置这些属性:
属性名:默认值//说明
FileName:"",//文件上传控件的name,配合后台使用
FrameName:"",//iframe的name,要自定义iframe的话这里设置name
onIniFile:function(){},//整理文件时执行(其中参数是file对象)
onEmpty:function(){},//文件空值时执行
Limit:0,//文件数限制,0为不限制
onLimite:function(){},//超过文件数限制时执行
Distinct:true,//是否不允许相同文件
onSame:function(){},//有相同文件时执行
ExtIn:[],//允许后缀名
onNotExtIn:function(){},//不是允许后缀名时执行
ExtOut:[],//禁止后缀名,当设置了ExtIn则ExtOut无效
onExtOut:function(){},//是禁止后缀名时执行
onFail:function(){},//文件不通过检测时执行(其中参数是file对象)
onIni:function(){}//重置时执行
使用方法可以参考实例。
程序中提供了下面几个方法:
Ini 整理空间
Check 检测file对象
Delete 删除指定file
Clear 删除全部file
完整程序
样式设置
Code
.fu_list {
width:600px;
background:#ebebeb;
font-size:12px;
}
.fu_list td {
padding:5px;
line-height:20px;
background-color:#fff;
}
.fu_list table {
width:100%;
border:1px solid #ebebeb;
}
.fu_list thead td {
background-color:#f4f4f4;
}
.fu_list b {
font-size:14px;
}
/*file容器样式*/
a.files {
width:90px;
height:30px;
overflow:hidden;
display:block;
border:1px solid #BEBEBE;
background:url(img/fu_btn.gif) left top no-repeat;
text-decoration:none;
}
a.files:hover {
background-color:#FFFFEE;
background-position:0 -30px;
}
/*file设为透明,并覆盖整个触发面*/
a.files input {
margin-left:-350px;
font-size:30px;
cursor:pointer;
filter:alpha(opacity=0);
opacity:0;
}
/*取消点击时的虚线框*/
a.files, a.files input {
outline:none;/*ff*/
hide-focus:expression(this.hideFocus=true);/*ie*/
}
html代码
Code
<form id="uploadForm" action="File.ashx">
<table border="0" cellspacing="1" class="fu_list">
<thead>
<tr>
<td colspan="2"><b>上传文件</b></td>
</tr>
</thead>
<tbody>
<tr>
<td align="right" width="15%" style="line-height:35px;">添加文件:</td>
<td><a href="javascript:void(0);" class="files" id="idFile"></a> <img id="idProcess" style="display:none;" src="img/loading.gif" /></td>
</tr>
<tr>
<td colspan="2"><table border="0" cellspacing="0">
<thead>
<tr>
<td>文件路径</td>
<td width="100"></td>
</tr>
</thead>
<tbody id="idFileList">
</tbody>
</table></td>
</tr>
<tr>
<td colspan="2" style="color:gray">温馨提示:最多可同时上传 <b id="idLimit"></b> 个文件,只允许上传 <b id="idExt"></b> 文件。 </td>
</tr>
<tr>
<td colspan="2" align="center" id="idMsg"><input type="button" value="开始上传" id="idBtnupload" disabled="disabled" />
<input type="button" value="全部取消" id="idBtndel" disabled="disabled" />
</td>
</tr>
</tbody>
</table>
</form>
程序代码
Code
var isIE = (document.all) ? true : false;
var $ = function (id) {
return "string" == typeof id ? document.getElementById(id) : id;
};
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var Extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
}
var Bind = function(object, fun) {
return function() {
return fun.apply(object, arguments);
}
}
var Each = function(list, fun){
for (var i = 0, len = list.length; i < len; i++) { fun(list[i], i); }
};
//
var FileUpload = Class.create();
FileUpload.prototype = {
//表单对象,文件控件存放空间
initialize: function(form, folder, options) {
this.Form = $(form);//表单
this.Folder = $(folder);//文件控件存放空间
this.Files = [];//文件集合
this.SetOptions(options);
this.FileName = this.options.FileName;
this._FrameName = this.options.FrameName;
this.Limit = this.options.Limit;
this.Distinct = !!this.options.Distinct;
this.ExtIn = this.options.ExtIn;
this.ExtOut = this.options.ExtOut;
this.onIniFile = this.options.onIniFile;
this.onEmpty = this.options.onEmpty;
this.onNotExtIn = this.options.onNotExtIn;
this.onExtOut = this.options.onExtOut;
this.onLimite = this.options.onLimite;
this.onSame = this.options.onSame;
this.onFail = this.options.onFail;
this.onIni = this.options.onIni;
if(!this._FrameName){
//为每个实例创建不同的iframe
this._FrameName = "uploadFrame_" + Math.floor(Math.random() * 1000);
//ie不能修改iframe的name
var oFrame = isIE ? document.createElement("<iframe name=\"" + this._FrameName + "\">") : document.createElement("iframe");
//为ff设置name
oFrame.name = this._FrameName;
oFrame.style.display = "none";
//在ie文档未加载完用appendChild会报错
document.body.insertBefore(oFrame, document.body.childNodes[0]);
}
//设置form属性,关键是target要指向iframe
this.Form.target = this._FrameName;
this.Form.method = "post";
//注意ie的form没有enctype属性,要用encoding
this.Form.encoding = "multipart/form-data";
//整理一次
this.Ini();
},
//设置默认属性
SetOptions: function(options) {
this.options = {//默认值
FileName: "",//文件上传控件的name,配合后台使用
FrameName: "",//iframe的name,要自定义iframe的话这里设置name
onIniFile: function(){},//整理文件时执行(其中参数是file对象)
onEmpty: function(){},//文件空值时执行
Limit: 0,//文件数限制,0为不限制
onLimite: function(){},//超过文件数限制时执行
Distinct: true,//是否不允许相同文件
onSame: function(){},//有相同文件时执行
ExtIn: [],//允许后缀名
onNotExtIn: function(){},//不是允许后缀名时执行
ExtOut: [],//禁止后缀名,当设置了ExtIn则ExtOut无效
onExtOut: function(){},//是禁止后缀名时执行
onFail: function(){},//文件不通过检测时执行(其中参数是file对象)
onIni: function(){}//重置时执行
};
Extend(this.options, options || {});
},
//整理空间
Ini: function() {
//整理文件集合
this.Files = [];
//整理文件空间,把有值的file放入文件集合
Each(this.Folder.getElementsByTagName("input"), Bind(this, function(o){
if(o.type == "file"){ o.value && this.Files.push(o); this.onIniFile(o); }
}))
//插入一个新的file
var file = document.createElement("input");
file.name = this.FileName; file.type = "file"; file.onchange = Bind(this, function(){ this.Check(file); this.Ini(); });
this.Folder.appendChild(file);
//执行附加程序
this.onIni();
},
//检测file对象
Check: function(file) {
//检测变量
var bCheck = true;
//空值、文件数限制、后缀名、相同文件检测
if(!file.value){
bCheck = false; this.onEmpty();
} else if(this.Limit && this.Files.length >= this.Limit){
bCheck = false; this.onLimite();
} else if(!!this.ExtIn.length && !RegExp("\.(" + this.ExtIn.join("|") + ")$", "i").test(file.value)){
//检测是否允许后缀名
bCheck = false; this.onNotExtIn();
} else if(!!this.ExtOut.length && RegExp("\.(" + this.ExtOut.join("|") + ")$", "i").test(file.value)) {
//检测是否禁止后缀名
bCheck = false; this.onExtOut();
} else if(!!this.Distinct) {
Each(this.Files, function(o){ if(o.value == file.value){ bCheck = false; } })
if(!bCheck){ this.onSame(); }
}
//没有通过检测
!bCheck && this.onFail(file);
},
//删除指定file
Delete: function(file) {
//移除指定file
this.Folder.removeChild(file); this.Ini();
},
//删除全部file
Clear: function() {
//清空文件空间
Each(this.Files, Bind(this, function(o){ this.Folder.removeChild(o); })); this.Ini();
}
}
测试代码
Code
var fu = new FileUpload("uploadForm", "idFile", { Limit: 3, ExtIn: ["jpg", "gif"],
onIniFile: function(file){ file.value ? file.style.display = "none" : this.Folder.removeChild(file); },
onEmpty: function(){ alert("请选择一个文件"); },
onLimite: function(){ alert("超过上传限制"); },
onSame: function(){ alert("已经有相同文件"); },
onNotExtIn: function(){ alert("只允许上传" + this.ExtIn.join(",") + "文件"); },
onFail: function(file){ this.Folder.removeChild(file); },
onIni: function(){
//显示文件列表
var arrRows = [];
if(this.Files.length){
var oThis = this;
Each(this.Files, function(o){
var a = document.createElement("a"); a.innerHTML = "取消"; a.href = "javascript:void(0);";
a.onclick = function(){ oThis.Delete(o); return false; };
arrRows.push([o.value, a]);
});
} else { arrRows.push(["<font color='gray'>没有添加文件</font>", " "]); }
AddList(arrRows);
//设置按钮
$("idBtnupload").disabled = $("idBtndel").disabled = this.Files.length <= 0;
}
});
$("idBtnupload").onclick = function(){
//显示文件列表
var arrRows = [];
Each(fu.Files, function(o){ arrRows.push([o.value, " "]); });
AddList(arrRows);
fu.Folder.style.display = "none";
$("idProcess").style.display = "";
$("idMsg").innerHTML = "正在添加文件到您的网盘中,请稍候……<br />有可能因为网络问题,出现程序长时间无响应,请点击“<a href='?'><font color='red'>取消</font></a>”重新上传文件";
fu.Form.submit();
}
//用来添加文件列表的函数
function AddList(rows){
//根据数组来添加列表
var FileList = $("idFileList"), oFragment = document.createDocumentFragment();
//用文档碎片保存列表
Each(rows, function(cells){
var row = document.createElement("tr");
Each(cells, function(o){
var cell = document.createElement("td");
if(typeof o == "string"){ cell.innerHTML = o; }else{ cell.appendChild(o); }
row.appendChild(cell);
});
oFragment.appendChild(row);
})
//ie的table不支持innerHTML所以这样清空table
while(FileList.hasChildNodes()){ FileList.removeChild(FileList.firstChild); }
FileList.appendChild(oFragment);
}
$("idLimit").innerHTML = fu.Limit;
$("idExt").innerHTML = fu.ExtIn.join(",");
$("idBtndel").onclick = function(){ fu.Clear(); }
//在后台通过window.parent来访问主页面的函数
function Finish(msg){ alert(msg); location.href = location.href; }
【asp版本补充】
由于很多人问我asp版本的后台该如何写,所以决定写一个给大家。
这里我用了化境HTTP上传程序2.1版(应该是最新版了)的无组件上传类,但用的时候发现几个问题(不知是我不会用还是asp本身的问题):
1,当file控件的name是空时,后台会找不到文件;
2,文件名比较短时(例如我用"f"),后台也找不到文件;
3,当有多个file控件,如果使用相同的name,后台只会保存一个文件;
4,我在上传文件后输出的中文是乱码(有时又正常)。
针对前3条,我加了一个RanName属性,设为true的话会自动生成随机的file控件名,对于第4条,我发现如果字是直接写在文档上就不会乱码,所以我这里把输出的文字都直接写在文档上没有用变量。如果有兄弟知道怎么解决这些问题记得告诉我哦。
精华的评论:
回复 引用 查看 2008-10-20 09:21 | 梦在天涯
ding!
回复 引用 查看 2008-10-20 09:22 | Selfocus
我来占个沙发,坐到慢慢看!
回复 引用 2008-10-20 09:33 | mr.s [未注册用户]
不错啊,iframe确实是好东西,我以前也用它做过一些无刷新的效果。
不过现在有些浏览器或用户怕iframe中藏有病毒,禁用了iframe那怎么办啊。
回复 引用 查看 2008-10-20 10:31 | cloudgamer
@mr.s
@Selfocus
谢谢支持
回复 引用 查看 2008-10-20 10:33 | cloudgamer
@mr.s
iframe应用很多,一般不会禁的吧,如果怕禁了就不用无刷新咯
代码中插了个form导致刚才评论提交不了!-_-
回复 引用 查看 2008-10-20 10:50 | jww
JS代码看着很舒服,向lz学习!
回复 引用 查看 2008-10-20 10:54 | 沛沛
当上传时,点取消,不应该让整个页面刷新嘛
这样会导致页面中其他表单中的未提交数据丢失
回复 引用 查看 2008-10-20 10:56 | Airker
下载了!回去看看 学习学习!
回复 引用 查看 2008-10-20 10:58 | cloudgamer
@沛沛
这个是程序中自己设置的,并不是规定的,完全可以去掉。
@jww
@Airker
谢谢支持
回复 引用 查看 2008-10-20 10:58 | 一线风
记一下~
回复 引用 查看 2008-10-20 11:03 | fkeuem
真的很不错。谢谢。
回复 引用 查看 2008-10-20 11:20 | PuserChen
下载了,学习下! 多谢搂主
回复 引用 查看 2008-10-20 11:23 | guojing
为什么不使用AJAX+上传控件呢 也可以实现无刷新
回复 引用 查看 2008-10-20 11:44 | cloudgamer
@一线风
@fkeuem
@PuserChen
谢谢支持
@guojing
AJAX+上传控件其实也是相同的原理
你也可以用啊,我这里没有讨厌控件的意思啊
这里只是教大家怎么自己动手做这个功能
回复 引用 查看 2008-10-20 13:07 | 吴畏
我是新手,对下面代码有些不解
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var FileUpload = Class.create();
FileUpload.prototype = {
//表单对象,文件控件存放空间
initialize: function(form, folder, options) {}}
Class.create() return的是一个Function对象,为什么网上的资料说Class.create()的是一个类,到底javascript里面的类是什么样的概念,望解答,谢谢
回复 引用 查看 2008-10-20 13:47 | cloudgamer
@吴畏
js的类其实就是Function对象
因为js没有真正的类,只是用Function对象模拟的
回复 引用 查看 2008-10-20 13:56 | 王国金
谢谢楼主的无限!
下过来,可以用在项目中。呵呵。
回复 引用 查看 2008-10-20 14:33 | 克隆
不错,记号!
回复 引用 查看 2008-10-20 14:38 | Charles Chen
--引用--------------------------------------------------
guojing: 为什么不使用AJAX+上传控件呢 也可以实现无刷新
--------------------------------------------------------
asp.net ajax中用UpdatePanel异步回传中用上传控件好像后台无法访问到上传控件中的值呢?有什么好的解决方案呢?请各位指教?
回复 引用 查看 2008-10-20 15:40 | 花香
下载下来试了下,总是出现 "名称以无效字符开头"的javascript错误
,麻烦看下
文件路径是
C:\Documents and Settings\Administrator\桌面\1.jpg
回复 引用 查看 2008-10-20 15:50 | cloudgamer
@王国金
@克隆
谢谢支持
@Charles Chen
这个我也不清楚,因为我都没用过
@花香
这个不是js错误,你去google一下吧
因为我这里没有出现这个问题
可能是你的iis处理ashx文件时出现问题
回复 引用 查看 2008-10-20 17:33 | ghd2004
不错!
3Q
回复 引用 查看 2008-10-21 00:02 | cloudgamer
@ghd2004
谢谢支持
回复 引用 查看 2008-10-21 20:00 | 一舟
greate!
回复 引用 查看 2008-10-21 21:21 | OOLi
很好,谢谢,可以学到好多东西啊,我是新手,谢谢
回复 引用 查看 2008-10-21 21:22 | OOLi
为什么有的地方用两个!!啊
回复 引用 查看 2008-10-21 21:44 | OOLi
新手,请问 var a=function(){};
和a:function(){}有什么区别啊,后面这种形式什么时侯用到啊,谢谢啊
回复 引用 查看 2008-10-21 23:15 | cloudgamer
@一舟
@OOLi
谢谢支持
用!!是为了保证返回的是bool值
var a=function(){};效果跟function a(){}类似
a:function(){}是用在对象结构中,例如:
var b={a:function(){}}
回复 引用 2008-10-22 10:18 | humbass [未注册用户]
能不能写个asp的示例啊
回复 引用 2008-10-22 12:32 | 来客 [未注册用户]
不错学习
回复 引用 查看 2008-10-22 16:02 | cloudgamer
@humbass
@来客
谢谢支持
asp实例已经放出
回复 引用 查看 2008-10-22 17:16 | jolboy
好东西!佩服!
回复 引用 查看 2008-10-22 17:25 | 倾叶枫城
学习了,谢谢
回复 引用 查看 2008-10-22 19:39 | cloudgamer
@jolboy
@倾叶枫城
谢谢支持
回复 引用 查看 2008-10-23 08:58 | Jon.Hong
支持大文件吗?
回复 引用 查看 2008-10-23 09:00 | cloudgamer
@Jon.Hong
这个没研究
估计太大的不行
回复 引用 查看 2008-10-23 10:48 | 张玉峰
呵呵,很不错的东东呀,大文件主要是IIS的限制吧
回复 引用 查看 2008-10-23 11:12 | cloudgamer
@张玉峰
大文件上传显示进度条网上有文章介绍
但没找到实例
那位兄弟有的话发一份给我吧
回复 引用 查看 2008-10-23 14:22 | 小老好
不错,晚上回家在看看!
回复 引用 查看 2008-10-23 18:35 | cloudgamer
@小老好
谢谢支持
回复 引用 查看 2008-10-24 15:41 | 漂零
大哥,太强了。学习
回复 引用 2008-10-25 10:57 | gdjlc [未注册用户]
感觉楼主的每篇文章都很经典,忍不住进来赞一句~~
回复 引用 2008-10-25 11:13 | 进来学习 [未注册用户]
下载下来试了下,总是出现 "名称以无效字符开头"的javascript错误
,麻烦看下
文件路径是
C:\Documents and Settings\Administrator\桌面\1.jpg
-----------------------------------------------
我也一样啊,直接打开点"上传"没反应.
把它放在VS在打开,点"上传"后一直显示上传的loading.gif图片,等了四五分钟还是在上传中,请LZ发发慈悲看一下 :)
(是.NET版本的)
回复 引用 2008-10-25 11:21 | 进来学习 [未注册用户]
知道原因了,是自己太懒了,没去仔细看代码,
//保存文件 file.SaveAs(System.Web.HttpContext.Current.Server.MapPath("./file/" + Path.GetFileName(file.FileName)));
这里的路径改为自己的就行了...
回复 引用 查看 2008-10-25 19:37 | 兴百放
不错,不错,有时间可以交流一下
回复 引用 查看 2008-10-25 21:44 | cloudgamer
@漂零
@gdjlc
@兴百放
@进来学习
谢谢支持
回复 引用 查看 2008-10-25 23:23 | 愚公-
【asp版本补充】
由于很多人问我asp版本的后台该如何写,所以决定写一个给大家。
这里我用了化境HTTP上传程序2.1版(应该是最新版了)的无组件上传类,但用的时候发现几个问题(不知是我不会用还是asp本身的问题):
1,当file控件的name是空时,后台会找不到文件;
◆没有name的话,浏览器不会发送文件数据。
2,文件名比较短时(例如我用"f"),后台也找不到文件;
◆这个是程序错误,或者你用的无组件上传类有BUG
3,当有多个file控件,如果使用相同的name,后台只会保存一个文件;
◆网上流传的各种ASP无组件上传类共同的BUG,需要手动修改,改为多个同名控件时存为数组。
4,我在上传文件后输出的中文是乱码(有时又正常)。
◆你程序的字符集问题。明确指定响应字符集,切源文件保存的编码与响应字符集一致,就没问题。
回复 引用 查看 2008-10-25 23:36 | cloudgamer
@愚公-
.net的例子就是没有name的也能发送哦
回复 引用 2008-10-26 09:03 | 举荐 [未注册用户]
其实jsp般也和file的name无关的,大家可以试试啊
回复 引用 查看 2008-10-26 17:28 | cloudgamer
@举荐
有没有name,file都会传送到后台
关键是后台如何获取这个文件
回复 引用 2008-10-28 09:44 | loo [未注册用户]
问下楼主的jsp版本都实现了什么功能,可不可以 就是在本地浏览个文件 然后点击上传,然后就上传到服务器的制定目录上?
回复 引用 查看 2008-10-28 10:37 | cloudgamer
@loo
可以的啊
这个系统就是实现这个功能
记得修改文件保存路径
回复 引用 2008-10-29 13:42 | 学习生 [未注册用户]
为什么我下的。NET没办法上传?
其他的都可以,如ASP的都可以,
回复 引用 查看 2008-10-29 13:51 | cloudgamer
@学习生
看看路径有没有问题
我写的版本是asp.net 2.0的
还有就是看看是不是机器配置问题,因为其他人都能正常运行的
回复 引用 2008-10-29 15:02 | springhcq [未注册用户]
有机会要多向您学习
如果不嫌弃,可以加我为好友:123347564
回复 引用 2008-10-29 17:24 | wei_wei [未注册用户]
ASP版本有BUG,无法自动命名文件名,上传两个(含)以上的文件,提示上传完成,但其实一个文件都没上传。
回复 引用 查看 2008-10-29 21:12 | cloudgamer
@springhcq
一起学习
不过我不怎么上q,有什么问题可以留言
@wei_wei
我试了一下
没问题啊
你确定你下的是asp版而且没有修改程序?
回复 引用 2008-10-30 00:09 | conqweal [未注册用户]
谢谢LZ的贡献精神;
不过FLASH结合支持多个文件直接选取,
似乎是未来的方向.
回复 引用 查看 2008-10-30 00:26 | cloudgamer
@conqweal
FLASH跟js不是同一个东西
我觉得没必要把这两个东西进行比较
各自都有自己的发展的
回复 引用 查看 2008-11-02 15:52 | Freedom~Jun
文章不错,收藏慢慢看
回复 引用 查看 2008-11-03 15:54 | 张荣华
@cloudgamer
关于iframe的创建其实可以不用浏览器检测,可以利用innerHTML属性来完成:
var frameContainer = document.createElement('div'); //创建一个用来放frame的空div
frameContainer.innerHTML = ' <iframe name="frame1" id="frame1" ></frame>';
document.body.appendChild(frameContainer);
回复 引用 2008-11-03 16:45 | 测试566 [未注册用户]
上传中文文件名的文件,虽上传成功,单文件名变成乱码了,如何解决!
回复 引用 查看 2008-11-03 23:00 | cloudgamer
@Freedom~Jun
谢谢支持
@测试566
这个可能是前后台编码不一致的问题,你可以统一一下编码,或者后台重新定一个名也可以
@张荣华
这样就要加一个div
还是不太好
回复 引用 2008-11-04 15:35 | Fans [未注册用户]
var Each = function(list, fun){
for (var i = 0, len = list.length; i < len; i++) { fun(list[i], i); }
};
朋友 你能不能给我说下这句话的作用,还有一般什么时候用? 有什么好处!麻烦了
回复 引用 查看 2008-11-04 16:17 | cloudgamer
@Fans
就是对list数组的每个元素作为fun函数的参数执行一次
这样说明白吗
回复 引用 2008-11-04 17:01 | 张帅 [未注册用户]
JSP版的读不到文件流呀,在file.jsp中的第一个while循环就退出了,这是怎么回事。
回复 引用 查看 2008-11-04 17:44 | cloudgamer
@张帅
这个别人测试过应该可以的
因为我不懂jsp也没办法解答你
回复 引用 2008-11-05 10:46 | 张帅1 [未注册用户]
@cloudgamer
哦~!
回复 引用 2008-11-06 14:21 | bopo [未注册用户]
你好,我想问一下,我运行.NET实例一无法添加文件,一看提示我已存在该文件,可是什么都没有啊。我清除缓存就是重启的没有变化,请问是什么问题,如何解决?
回复 引用 查看 2008-11-06 14:36 | cloudgamer
@张帅1
呵呵
@bopo
但我这里测试没发现这个问题啊
确定没修改我的程序?
回复 引用 2008-11-06 15:31 | bopo [未注册用户]
是啊。 没有添加任何代码。
就是直接运行你提供的.NET实例。
回复 引用 2008-11-06 15:36 | bopo [未注册用户]
对了,我使用的是IE8。
会不会程序在这个环境下有什么问题。
回复 引用 2008-11-06 15:55 | Fans [未注册用户]
兄弟
var Each = function(list, fun){
for (var i = 0, len = list.length; i < len; i++) { fun(list[i], i); }
};
是不是直接产生闭包了呢?
还有我想知道您博客的预览功能是怎么弄的?
回复 引用 2008-11-06 16:00 | bopo [未注册用户]
下面这句是判断是否有相同文件吧?
if(o.value == file.value){ bCheck = false; }
我分别alert(o.value),alert(file.value)
看到他们两值却是一样,可是我是添加第一个文件呢。
回复 引用 查看 2008-11-06 16:08 | cloudgamer
@bopo
ie8的话我就没测试过了
你在这里的效果预览中有没有这个情况
你用ff试试
@Fans
我想你的理解是对的,主要还是想省点代码
至于预览功能,这个blog本身就可以直接贴html代码
回复 引用 2008-11-06 16:35 | bopo [未注册用户]
一、转了台机方试了试,确实IE8下有问题啊~
二、FF我也试了,文件倒是能上传成功(我到目录下去看以存在),但是界面还是一直停在请等待。
回复 引用 查看 2008-11-06 17:12 | cloudgamer
@bopo
我用ff3和ie7测试过
没有问题哦
回复 引用 查看 2008-11-06 18:26 | 谢小妹
好的,记下了!!!
回复 引用 2008-11-08 15:17 | own [未注册用户]
谢谢
回复 引用 2008-11-08 15:18 | own [未注册用户]
以后多多交流!我的q是105940137
回复 引用 2008-11-09 22:09 | 小可66 [未注册用户]
不错 支持一下
回复 引用 2008-11-11 09:40 | noihsi [未注册用户]
很不错的效果,只是提交页面给程序员的时候,他们说不能跟 JSP 后台连接上,真是有点晕!!多谢!!
回复 引用 查看 2008-11-11 10:36 | cloudgamer
@谢小妹
@own
@小可66
谢谢支持
@noihsi
jsp应该也行的
详细看例子吧我不怎么懂jsp
回复 引用 2008-11-14 14:31 | poisson123 [未注册用户]
在FileUpload.prototype的initialize定义中
this.Distinct = !!this.options.Distinct
为什么前面要打两个!非?
可不可以直接
this.Distinct = this.options.Distinct;代替呢?
回复 引用 查看 2008-11-14 14:48 | cloudgamer
@poisson123
用!!可以保证返回的是bool值
回复 引用 2008-11-15 20:18 | qingren [未注册用户]
怎么写进数据库啊????????????
回复 引用 2008-11-15 20:20 | 多多大人 [未注册用户]
我发现发上传的都不能重新命名,如果是重名,就只保存最后一个
回复 引用 查看 2008-11-16 00:29 | cloudgamer
@qingren
写什么进数据库?
@多多大人
不知你是说哪个版本
我这里是直接用原来的文件名作为文件名
你可以在程序中改名再保存文件的
回复 引用 2008-11-19 13:56 | hier [未注册用户]
我有个问题,asp.net开发的时候上传一般不会在一个htm页面上,如果一个aspx页套用了模板页,请问怎么将aspx;form里面的action指向'ashx'文件了?!
回复 引用 查看 2008-11-19 14:13 | cloudgamer
@hier
生成的页面的form肯定有一个id的
你把这个id作为实例化的参数并用js修改它的action
不过之后页面就不能正常提交了
回复 引用 2008-11-19 14:44 | hier [未注册用户]
呵呵,楼主东西做的很好,但是更多的应该想想应用方面的要不只能当"花瓶"了。
回复 引用 查看 2008-11-19 15:02 | cloudgamer
@hier
也不是花瓶啊
这是我们开发的网盘的文件上传部分
asp.net开发也不是不要html啊
而且也可以用其他方法绕过提交的问题
例如插入一个新的form作为提交form,或者把文件上传页面作为iframe插入等等
而且我后台方面很菜,说不定从后台的方向想有好的解决方法(例如弄成一个控件什么的)
我想这里只是一个例子教学,运用时还是自己多思考一下吧
回复 引用 查看 2008-11-22 08:51 | fengsky491
学习了。
问问怎么收藏博主的文章,嘿嘿。。。
回复 引用 查看 2008-11-23 00:01 | amingo
我现在有这样一个需求:
针对每一个附件有一个附件类型字段,比如:工作报告,问题总结等。
这就需要在每添加一个附件时,旁边要有一个下拉菜单来选择附加类型。
但如何将附件和这个附件的类型一起action到提交页面呢??而且附件和类型要一一对应。
回复 引用 查看 2008-11-23 00:20 | Hunts.C
哇塞,顶~!
回复 引用 查看 2008-11-23 01:56 | cloudgamer
@fengsky491
@Hunts.C
谢谢支持
@amingo
这个我确实没想到很好的方法
我有一个思路是添加一个file后
根据file的value添加一个类型选择,例如把value作为类型选择的name
这样提交到后台后就能用FileName获取对应的类型数据
不过没有实践过不是是否可行
回复 引用 2008-11-25 17:25 | 木木1983 [未注册用户]
无奈啊...多文件同时上传.为什么只成功一个..我倒....
回复 引用 查看 2008-11-25 19:14 | cloudgamer
@木木1983
你说的是哪个版本
回复 引用 2008-11-28 14:38 | 剑无名 [未注册用户]
看了博主的文章,很受用。我利用博主此文实现的效果,结合DWR做了一个JAVA版的无刷新上传,在仿网盘效果之上加入了上传进度条的功能,在这里打个广告:http://blog.163.com/linlong0613@126/blog/static/625258562008102804829960/。希望对java开发的朋友有帮助。谢谢博主大哥的辛勤劳动!
回复 引用 查看 2008-11-28 16:10 | Daniel_Wu
喜欢简单的方法。
回复 引用 2008-12-01 01:07 | dragon_zhang [未注册用户]
jsp的路径那里是不是必须给绝对路径,我给了相对路径怎么不好使!
回复 引用 查看 2008-12-01 08:23 | cloudgamer
@Daniel_Wu
@dragon_zhang
谢谢支持
你把相对路径转成绝对路径就行了
jsp应该有这样的方法吧
我不太懂jsp
回复 引用 2008-12-01 14:39 | 我是小菜 [未注册用户]
请问怎么设置文件大小的限制啊
回复 引用 2008-12-01 14:48 | 我是小菜 [未注册用户]
请楼主帮帮忙,谢谢
回复 引用 查看 2008-12-01 15:06 | cloudgamer
@我是小菜
由于安全问题文件大小一般来说在客户端是不能检测得到的,如果是图片的话可以用下面这个方法获取(ie6)
<img id=img1 src="" onload='alert("图片大小"+img1.fileSize/1024+"K");'>
<input type=file name="file1" onchange="img1.src=this.value">
回复 引用 2008-12-01 20:08 | 我是小菜 [未注册用户]
首先感谢楼主的回复,在您没有回复的时候,我自己试了一下这个
var fso = new ActiveXObject('Scripting.FileSystemObject');
var file1 = fso.GetFile(file.value);
file1.Size 就能返回,但这存在安全性的问题,必须在IE的安全里把“没有设为安全的active”那个启用,所以还是楼主高,呵呵,我在现在代码的基础上加了一Length和onLength,向你学习
回复 引用 查看 2008-12-02 08:23 | cloudgamer
@我是小菜
那个一般客户端都是禁止的
所以一般行不通
回复 引用 查看 2008-12-03 17:32 | shawnliu
qiang
回复 引用 查看 2008-12-04 15:14 | 醉春风
楼主写的这个不错。
有一个小地方,“添加文件”按钮问题。IE8中又变成了“浏览”字样。
回复 引用 查看 2008-12-04 15:31 | cloudgamer
@shawnliu
@醉春风
谢谢支持
这个ie8的问题因为我没有ie8也测试不了
而且ie8正式版都还没出呢
到时再看吧
posted on 2008-12-08 11:04
CopyHoo 阅读(706)
评论(0) 编辑 收藏 所属分类:
JavaScript