由于技术和经验有限,我一直不敢把文章发到首页(除了第一次发文不清楚状况点错了之外)。这次壮着胆子喝了二两雪碧,我也豁出来了,大家看了要是觉得没什么技术含量的话,就当这篇文章是个小笑话也就是了~
在做这个功能之前参考了很多网上的文章,能试的基本都试了,发现没有完全正确的,大部分也是转来转去的,比如:《JQuery+ajax实现批量上传图片》。实现的思想就是客户端选好了路径,传到服务器端接收然后实现上传。
这样在本地做是没有任何问题的,(因为本地做服务器和客服端都是你自己的电脑)
而大部分的人看到这基本也不会真正的拿来用然后发布到网上,只是在本地练练手而已。
但是一旦你发布之后,就会发现这个思路从根本上就是错的:用服务器端的方法接收客户端的文件路径,再用这个路径找文件,根本就找不到。因为服务器找到的是服务器本机的文件。(服务器和客户端已经分离)
而实现了这个功能的网站基本都找不到相应的实现细节,(比如曾经的网易网盘功能,还有现在的摇篮网的space空间上传)
在这里抛砖引玉,希望大家一起讨论,也希望有网盘和批量上传经验的兄弟多多指点。
先看看我要实现的功能和效果:(上传完毕会生成成功和失败两个文件夹)(这已经是我发布到内网服务器后的效果)
图
流程:
点击自动上传--弹出浮动窗口,展示本地磁盘--选择磁盘--列出该磁盘下所有文件夹--选择一个文件夹--点击确定--上传
这里一个麻烦之处就是由于种种原因,你无法使用服务器端的fileupload控件或者客户端的input type=file控件
这样的话就无法在服务器端用Request.File接收文件,那该如何实现?
由于用户要求选择文件夹而不是文件,所以我用不了任何控件,只能通过js构建本机文件目录,
最开始我使用了一个Jquery插件:jqueryFileTree。
插件详细和下载请见:http://abeautifulsite.net/2008/03/jquery-file-tree/ 。里面有使用说明和demo(英文,是个老外做的),老实说效果很好,只不过不适合我要实现的功能(用户选择服务器文件有何用?)
实现原理是通过ajax在cs方法中构建目录串输出html。这里就遇到了之前提到的问题,用服务器方法只能获取服务器的文件路径,发布之后无效。所以无法通过ajax方式获取文件的html串,只能用纯js构建。
我改造一下这个插件,这里要使用一个ActiveXObject:Scripting.FileSystemObject,是js中用来访问本机文件的插件。网上可以搜到这个插件下的方法和属性,但是不够全面,很多方法要通过自己调试js时查看变量方法和属性列表的方法找到。利用这个插件我改造了jqueryFileTree,让它变成一个纯js访问本机文件目录。
注:此ActiveXObject的用法参照了这篇文章:http://www.cnblogs.com/dwjaissk/archive/2007/02/28/659153.html
里面有很多Scripting.FileSystemObject下面的方法和属性。
主要改造了showTree方法:


$(c).addClass( ' wait ' );
$( " .jqueryFileTree.start " ).remove();
var a = new Array;
t = unescape(t);
var fso, f, fc, a;
fso = new ActiveXObject( " Scripting.FileSystemObject " );
$( ' #inputpath ' ).val(t);
f = fso.GetFolder(t);
fc = new Enumerator(f.SubFolders);
for (; ! fc.atEnd(); fc.moveNext()) {
a[a.length] = fc.item();
}
a = (a);
for ( var i = 0 ; i < a.length; i ++ ) {
var str = " <ul class='jqueryFileTree' style='display: none;'><li class='directory collapsed'><a href='#' rel=' " + a[i] + " /'> " + a[i] + " </a></li></ul> " ;
$(c).removeClass( ' wait ' ).append(str);
}
if (a.length < 1 ) {
$(c).removeClass( ' wait ' );
}
$(c).find( ' .start ' ).html( '' );
if (o.root == t) $(c).find( ' UL:hidden ' ).show();
else $(c).find( ' UL:hidden ' ).slideDown({ duration: o.expandSpeed, easing: o.expandEasing });
bindTree(c);
}
![]()
前台html
< form id ="form1" runat ="server" >
< div >
< div style ="width:978px;padding:1px;margin:10px auto;background:#00a8fe;" >
< div style ="border:1px solid #fff;font-size:14px;" >
< div style ="background:#edf9ff;height:35px;padding:0 15px 0 15px;line-height:35px" >
照片上传: < input id ="File1" runat ="server" name ="File1" size ="44" type ="file" />
< asp:Button ID ="Button_upload" runat ="server" Text ="上 传" onclick ="Button_upload_Click" />
< input id ="chkAutoUpload" type ="checkbox" onclick ="showOrhidden()" /> 自动上传
</ div >
</ div >
</ div >
<% -- 浮动层 -- %>
< div id ="divfloat" >
< a id ="closeX" href ="javascript:closethis()" onmouseover ="this.innerHTML='关闭'" onmouseout ="this.innerHTML='×'" > × </ a >
< div id ="divaddfirst" >
< input id ="lblselect" class ="inputlbl" type ="text" value ="选择需上传图片所在的文件夹" />
< input id ="lblweb" style ="width:230px;" disabled ="disabled" class ="inputlbl" type ="text" value ="(此功能需要加本站点为可信任站点)" />< br />
< span style ="color:Red; font-size:14px;" > 您选择的上传图片所在路径为: </ span >
< input id ="inputpath" class ="inputlbl" type ="text" value ="" disabled ="disabled" style =" width:320px;font-weight:bold; font-size:16px;" />
< input type ="checkbox" id ="chkIsTj" /> 特检照片
< ul >
< li id ="liC" class ="diskli" >
< img src ="Images/FolderImg/disk.jpg" alt ="" onclick ="choosefolder('C')" style ="cursor:hand;" />
< a id ="hrefC" style ="color:Black; font-size:medium " href ="javascript:choosefolder('C')" > 本地磁盘 (C:) </ a >
< div id ="divContainerC" class ="divContainer" ></ div >
</ li >
< li id ="liD" class ="diskli" >
< img src ="Images/FolderImg/disk.jpg" alt ="" onclick ="choosefolder('D')" style ="cursor:hand;" />
< a id ="hrefD" style ="color:Black; font-size:medium " href ="javascript:choosefolder('D')" > 本地磁盘 (D:) </ a >
< div id ="divContainerD" class ="divContainer" ></ div >
</ li >
< li id ="liE" class ="diskli" >
< img src ="Images/FolderImg/disk.jpg" alt ="" onclick ="choosefolder('E')" style ="cursor:hand;" />
< a id ="hrefE" style ="color:Black; font-size:medium " href ="javascript:choosefolder('E')" > 本地磁盘 (E:) </ a >
< div id ="divContainerE" class ="divContainer" ></ div >
</ li >
</ ul >
< a href ="javascript:selectconfirm()" style =" position:absolute; bottom:20px; left:300px; font-size:16px; font-weight:bold;" > 选择完毕 </ a >
< input id ="input2" class ="inputlbl" type ="text" value ="上传中,关闭当前页将停止上传...上传日志信息保存在网站根目录下Logs\AutoUploadPhoto_Exception.txt" disabled ="disabled" style =" display:none;position:absolute; bottom:10px; left:20px; width:600px; color:Red;" />
< input id ="hidinput" style ="display:none;" value ="0" />
</ div >
</ div >
< div id ="divfloatbg" ></ div >
</ div >
</ form >
浮动效果的实现网上有很多,方法:
![]()
浮动
//悬浮层显示
function showOrhidden()
{
var w = document.documentElement.clientWidth; //宽
var h = document.documentElement.clientHeight; //高
var tw = $("#divfloat").width();
var th = $("#divfloat").height();
$("#divfloat").css({ "position": "absolute", "top": h / 2 - th / 2, "left": w / 2 - tw / 2 });
if ($("#chkAutoUpload").attr("checked") == true)
{
$("#divfloatbg").css("opacity", "0.5"); //透明度
$("#divfloatbg").fadeIn("slow"); //缓慢淡出
$("#divfloat").fadeIn("slow");
}
else
{
$("#divfloatbg").fadeOut("slow");
$("#divfloat").fadeOut("slow");
}
}
点击文件夹按钮事件:
![]()
代码
// 选择本地文件夹上传相片
function choosefolder(disk)
{
$( " .divContainer " ).html( "" ); // 清空
var divContainer = ' #divContainer ' + disk;
// 构建div目录树
$(divContainer).fileTree({ root: disk + ' :\\ ' , multiFolder: false }, function (file)
{
// alert(file);
});
}
改造好之后已经可以实现这样的功能:传入一个盘符号,会列出所有该盘符的所有文件夹,继续点击会继续列出相应文件夹的子文件夹(和windows文件效果类似)。
接下来要实现上传。没有控件就不能直接接受file,又不能接收路径查找文件,那么只能把文件整个传到服务器端。思路是将文件转成base64流,再通过ajax传到服务器端(这里要用到另一个插件MSXML2.DOMDocument),然后服务器端Request.InputStream接收,然后用XML处理这个流,之后base64解码,然后继续你的上传流程就可以了。
![]()
代码
// 选择完成后确定按钮事件
function selectconfirm()
{
var localpath = $( " #inputpath " ).val();
var leaving = $( " #LblYue " ).text();
var tj = $( " #chkIsTj " ).attr( " checked " ) == true ? " 1 " : " 0 " ;
$( " #input2 " ).show();
$( " #hidinput " ).val( " 1 " );
upload(localpath, leaving, tj);
}
// 上传和文件夹操作
function upload(localpath, leaving, tj)
{
var fso, f, fc, a;
fso = new ActiveXObject( " Scripting.FileSystemObject " );
f = fso.GetFolder(localpath);
if ( ! fso.FolderExists(localpath + " \\Success " ))
{
fso.CreateFolder(localpath + " \\Success " );
}
if ( ! fso.FolderExists(localpath + " \\Fail " ))
{
fso.CreateFolder(localpath + " \\Fail " );
}
ff = new Enumerator(f.files);
for (; ! ff.atEnd(); ff.moveNext()) // 枚举所有文件
{
s = ff.item(); // 取文件对象
alert(s);
uptowebmethod(s.Path, leaving, tj, f.Path, localpath);
}
}
// 文件->流->XML->ajax传入后台
function uptowebmethod(path, leave, istj, fpath, localpath)
{
var ado_stream = new ActiveXObject( " ADODB.Stream " );
var xml_dom = createXMLDOM();
xml_dom.loadXML( ' <?xml version="1.0" ?><root/> ' );
xml_dom.documentElement.setAttribute( " xmlns:dt " , " urn:schemas-microsoft-com:datatypes " );
var l_node1 = xml_dom.createElement( " photo " );
l_node1.dataType = " bin.base64 " ;
ado_stream.Type = 1 ; // 1=adTypeBinary
ado_stream.Open();
ado_stream.LoadFromFile(path);
l_node1.nodeTypedValue = ado_stream.Read( - 1 ); // -1=adReadAll
ado_stream.Close();
xml_dom.documentElement.appendChild(l_node1);
var pnode = xml_dom.createElement( " path " );
xml_dom.documentElement.appendChild(pnode);
pnode.text = path;
var lnode = xml_dom.createElement( " leave " );
xml_dom.documentElement.appendChild(lnode);
lnode.text = leave;
var tjnode = xml_dom.createElement( " istj " );
xml_dom.documentElement.appendChild(tjnode);
tjnode.text = istj;
var fnode = xml_dom.createElement( " fpath " );
xml_dom.documentElement.appendChild(fnode);
fnode.text = fpath;
$.ajax({
type: " POST " , // 访问WebService使用Post方式请求
// contentType: "application/json", //WebService 会返回Json类型
url: " AjaxForm1.aspx " , // 调用WebService的地址和方法名称组合 ---- WsURL/方法名
processData: false ,
data: xml_dom, // 这里是要传递的参数,格式为 data: "{paraName:paraValue}",下面将会看到
// dataType: 'json',
success: function (result)
{ // 回调函数,result,返回值
if (result == ' 0 ' )
{
s.move(localpath + " \\Fail\\ " + s.name);
}
else if (data == ' 1 ' )
{
s.move(localpath + " \\Success\\ " + s.name);
}
else
{
alert(result);
}
}
})
}
// 创建XMLDOM
function createXMLDOM()
{
var arr = [ " MSXML2.DOMDocument.5.0 " , " MSXML2.DOMDocument.4.0 " , " MSXML2.DOMDocument.3.0 " , " MSXML2.DOMDocument " , " Microsoft.XmlDom " ];
for ( var i = 0 ; i < arr.length; i ++ )
{
try
{
var xmlDom = new ActiveXObject(arr[i]);
return xmlDom;
}
catch (oError)
{
alert( " error " );
}
}
throw new Error( " MSXML is not install on your system. " );
}
服务器端主要代码:
![]()
代码
#region Page_Load
protected void Page_Load( object sender, EventArgs e)
{
Stream photoStream = Request.InputStream;
XmlDocument doc = new XmlDocument();
if (Request.InputStream != null )
{
// byte[] _tmpData = new byte[photoStream.Length];
// photoStream.Read(_tmpData, 0, Convert.ToInt32(photoStream.Length));
// string request1 = System.Text.Encoding.UTF8.GetString(_tmpData);
doc.Load(photoStream);
XmlNode nod = doc.DocumentElement.SelectSingleNode( " path " );
Paths = nod.InnerText;
nod = doc.DocumentElement.SelectSingleNode( " fpath " );
string fpath = nod.InnerText;
nod = doc.DocumentElement.SelectSingleNode( " leave " );
string leaving = nod.InnerText;
nod = doc.DocumentElement.SelectSingleNode( " istj " );
string istj = nod.InnerText;
string photoXML = doc.DocumentElement.SelectSingleNode( " photo " ).InnerText;
byte [] photo = Convert.FromBase64String(photoXML);
// 去除xml中的多余标签和属性,转化为stream
MemoryStream stream = new MemoryStream();
XmlDocument docphoto = new XmlDocument();
char [] str = photoXML.ToCharArray();
for ( int i = 0 ; i < photo.Length; i ++ )
{
stream.WriteByte(photo[i]);
}
docphoto.Save(stream);
if ( ! string .IsNullOrEmpty(Paths))
{
// 存入cookie
HttpCookie cookie = Request.Cookies[ " Path " ];
if (cookie == null )
{
cookie = new HttpCookie( " Path " );
}
cookie[ " path " ] = fpath;
cookie[ " istj " ] = istj;
cookie.Expires = DateTime.Now.AddDays(100d);
Response.Cookies.Add(cookie);
UploadPh(Paths, leaving, istj, stream);//在此方法处理你的上传
}
}
}#endregion
目前此方法有三大问题:
1.不同的浏览器安全级别略有区别,activex经常会弹出提示,影响用户体验,有的还需要用户设置本站点为信任站点。
2.除IE外别的浏览器无法使用
3.IE有一个补丁对ADODB.Stream进行了限制使用,如果你一直开了自动更新或打了这个补丁,会弹出js错误:automation无法在服务器创建。其实就是ADODB.Stream无法创建对象造成的。
解决方法:打开注册表编辑器
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\
把{00000566-0000-0010-8000-00AA006D2EA4} 项删除或修改键值为任意值(除了400),这样你的ADODB.Stream就可以创建成功了。
css我就不贴了。大家有什么好的方案尽管拿出来~