最近在做一个webim,觉得之前看到的拖拽上传文件很方便,就决定加上这个功能,结果我一半的开发时间都花在这上面了,html5目前还是很坑爹啊。这里面涉及的主要是两个api,fileReader 和 XMLHttpRequest(以下简称xhr), firefox和chrome的较新版本都支持这两个接口。firefox对这两个接口支持的比较完美。而chrome版本的xhr只有send方法,没有sendasbinary方法,导致拖拽上传的二进制文件错乱,只能上传文本文件。有些教程说google的gears支持上传文件功能,试验了一下,用gears上传的时候崩溃,然后将chrome升级到12版本,发现chrome居然不支持gears了,再查了一下,google原来已经放弃了gears了,chrome的最后一根救命稻草是他的file input,原来可以把文件直接拖到他的<input type=file>上,最后的实现是在下层div放一个提示语句,上层div放file input,但是设置透明度为0,这样看起来和firefox的拖拽保持一致。下面放代码:
以下是chrome版本的实现
HTML部分:
<div class=”new_attachment” >
<form accept-charset=”UTF-8″ action=”/talks/1/attachments” enctype=”multipart/form-data” method=”post” target=”form_accepter”>
<div style=”margin:0;padding:0;display:inline”>
<input name=”authenticity_token” type=”hidden” value=”x2MqPKIsC+MgoQpiqx9k2sub62p+pqOcDzdKUXoxicA=” />
<!–这一段看不懂不用管,这个token是rails默认生成的安全策略,用来防止csrf攻击,后起之秀的安全策略做的是真好啊–>
</div>
<div class=”notice”>
<span>请将附件拖到这里上传</span>
</div>
<div class=”file_input”>
<input id=”attachment” name=”attachment” style=”width:100%; height:100%” type=”file” />
</div>
</form>
<iframe name=”form_accepter” style=”display:none”>
</iframe>
</div>
CSS部分:
.new_attachment {
margin: 200px auto 0px auto;
width: 194px;
height: 100px;
float: left;
border:3px dashed silver;
}
.new_attachment form {
height: 100%;
width: 100%;
}
.new_attachment .notice {
position: relative;
left: 0px;
top: 0px;
height: 100%;
}
.new_attachment .file_input {
position: relative;
left: 0px;
top: -100px;
height: 100%;
filter: alpha(opacity=50);
-khtml-opacity: 0;
}
js部分:
New = {
init: function () {
this.newAttachment = $( “div.new_attachment” );
this.attachmentForm = this.newAttachment.children( “form” );
this.fileInput = $( “div.file_input > input” );
this.noticeDiv = $( “div.notice” );
this.fileInput.change( function () {
Sentences.New.noticeDiv.children( “span” ).html( “正在上传请稍后” );
Sentences.New.attachmentForm.submit();
} );
}
$( document ).ready( function () {
Sentences.New.init();
} );
//用到了jquery,如果对jquery不熟悉的只能抱歉了,不过代码大概您应该能看懂
//还有些改进空间,例如gmail实现了上传的进度条,我暂时没找到实现的代码如果谁找到请告诉我一声,另外的提升空间是,监听html的drapenter事件,在有文件拖动到html中时显示那个提示div,诱导用户把文件拖拽到对应的位置。我实现的时候老收到莫名其妙的drapleave事件,所以我放弃了智能提示
firefox的实现
js部分
New = {
buildBody: function ( data, boundary ) {
var dashdash = ‘–’;
var crlf = ‘\r\n’;
var builder = ”;
/* 下面这段是rails用的,为了加上csrf认证通过,非rails绕过,我注释掉了,去的token的方法是通过页面里现有的form来取
builder += dashdash + boundary + crlf;
builder += “Content-Disposition: form-data; name=\”utf8\”" + crlf + crlf;
builder += ‘Content-Type: application/octet-stream’+ crlf + crlf;
builder += “✓″ + crlf;
builder += dashdash + boundary + crlf;
builder += “Content-Disposition: form-data; name=\”authenticity_token”
+ “\”" + crlf + crlf;
builder += $( “div.new_sentence > form > div > input” ).eq( 1 ).val()
+ “\r\n”;
*/
builder += dashdash+boundary+crlf;
builder += “Content-Disposition: form-data;” +
” name=\”attachment\”; filename=\”" +
New.attachment.fileName + “\”" + crlf;
builder += ‘Content-Type: application/octet-stream’+ crlf + crlf;
builder += data + crlf;
builder += dashdash + boundary + dashdash + crlf;
return builder;
},
uploadFile: function( fileData ) {
var boundary = ‘—————–’ + (new Date).getTime();
var data = New.buildBody( fileData, boundary );
this.xhr = new XMLHttpRequest();
//下面的这个url要您自己定制
var upload_url = “your upload url”;
this.xhr.open( “post”, upload_url, true);
this.xhr.onuploadprogress = New.onUploadProgress;
this.xhr.onreadystatechange = New.onUploadReadyStateChange;
this.xhr.setRequestHeader(“Content-type”, “multipart/form-data; boundary=” + boundary);
this.xhr.overrideMimeType(“text/plain; charset=x-user-defined-binary”);
if ( this.xhr.sendAsBinary ) {
this.xhr.sendAsBinary(data);
}
else {
this.xhr.send(data);
}
},
onUploadProgress: function( e ) {
if ( e.lengthComputable ) {
var percentage = Math.round((e.loaded * 100) / e.total);
$( “.preview” ).html( percentage / 100.0 );
}
},
onUploadReadyStateChange: function ( e ) {
if ( New.xhr.readyState == 4 ) {
//您要加的,上传完成后要做的事情
}
},
init: function () {
$( “div.new_sentence > form” ).submit( function () {
New.onSubmit();
} );
var uploader = $( “div.upload_box” );
$( “div.sentences” ).scrollTop( $( “div.sentences” )[0].
scrollHeight );
document.addEventListener( “dragenter”, function( e ){
uploader.css( “borderColor”, “gray” );
}, false );
document.addEventListener( “dragleave”, function( e ){
uploader.css( “borderColor”, “silver” );
}, false );
uploader.bind( “dragenter”, function( e ){
uploader.css( “borderColor”, “gray” );
uploader.css( “backgroundColor”, “white” );
}, false );
uploader.bind( “dragleave”, function( e ){
uploader.css( “backgroundColor”, “transparent” );
}, false );
uploader.bind( “dragenter”, function( e ){
e.stopPropagation();
e.preventDefault();
}, false );
uploader.bind( “dragover”, function( e ){
e.stopPropagation();
e.preventDefault();
}, false );
uploader[0].addEventListener( “drop”, function ( e ) {
e.stopPropagation();
e.preventDefault();
{
var file = e.dataTransfer.files[0];
New.attachment = file;
var dataReader = new FileReader();
dataReader.onload = function( e ) {
New.uploadFile( e.target.result );
};
dataReader.readAsBinaryString( file );
}
}, false );
}
};
$( document ).ready( function () {
New.init();
} );
html部分
<div class=”upload_box” style=”min-height:100px;border:3px dashed silver;”>
<div class=”preview”>
</div>
</div>
上面的代码在ff5和chrome12下都测试通过,如果有不对的地方,欢迎来邮讨论,shallwe@shallwe.net,或者留言亦可