2011年12月1日

 

很久没有写过博客了,闲的时候没东西可写,忙的时候没有时间写。

前些天,后台的同事提建议说,上传图片不是很好用,后台在线编辑器用的是fckeditor。
这时候想到了很久前看过一遍提升用户体验:HTML5 拖放文件上传,于是就打算做一个拖拽上传图片的功能。

因为是后台用,所以不用考虑ie的兼容了。

1.拖图片进浏览器的时候阻止浏览器的默认行为(比如打开直接图片)

  dropbox 给我们的容器添加上几个事件绑定dragenter,dragover,drop三个事件

 dropbox.addEventListener("dragenter", function(e){ e.stopPropagation(); e.preventDefault(); }, false); 
  dropbox.addEventListener("dragover" , function(e){ e.stopPropagation(); e.preventDefault(); }, false);
  dropbox.addEventListener("drop", function(e){
    e.stopPropagation(); //个人的理解是,若在dropbox上的drop事件被监听多次,则两外的事件绑定无效了(不知道对不对)
    e.preventDefault(); //阻止默认动作
    e.dataTransfer.files//一个file类型的数组,就是你拖拽进来的文件
  }, false);


2.在得到一个文件file后,要把它显示出来

预览图片要用到FileReader

1 var rd=new FileReader();
2 rd.onloadend=function(e){
3 var img=document.createElement('img');//创建一个图片
4 img.src=this.result;//result就是读出来的内容
5 img.width=100;
6 img.title=file.name;//文件的原始名称
7
8 }
9 rd.readAsDataURL({file}); // 读取为dataurl

Filereader有下面几种方法,预览图片用到的是readAsDataURL。

方法名参数描述
abort none 中断读取
readAsBinaryString file 将文件读取为二进制码
readAsDataURL file 将文件读取为 DataURL
readAsText file, [encoding] 将文件读取为文本

 

 

 

 

3.把图片发送到服务端,进行处理

 要提交到服务器,我们必须把图片读取为二进制的格式,这里就用到了Filereader的readAsBinaryString

 1 var reader = new FileReader(); 
2 reader.readAsBinaryString({file});
3
4 reader.onloadend = function(){
5 //bug(this.readyState); // 这个时候 应该是 2
6 //bug(this.result); //读取完成回调函数,数据保存在result中
7 var fileData=this.result;
8 var CRLF="\r\n";
9 var xhr = new XMLHttpRequest();
10 xhr.open('post', self.server, true);
11 //xhr.onreadystatechange=function(){};
12 var boundary='------OTkwNzI0OTEx----';//一段随机字符串,最好根据时间来生成,为了分割多个表单项
13 // 模拟一个文件提交请求
14 xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
15 //xhr.setRequestHeader("Content-Length", file.size); 在chrome下会出错,可能是出于安全考虑,不允许这样做
16 var body = '';
17 body += '--' + boundary + CRLF;
18 body += 'Content-Disposition: form-data; name="'+{fileFieldName}+'"; filename="' + file.name + '"'+CRLF;
19 body += "Content-Type: "+file.type+CRLF+CRLF;
20 body += fileData + CRLF;
21 body += "--" + boundary + "--"+CRLF;
22 xhr.onreadystatechange = function (aEvt) {
23 if (xhr.readyState == 4) {
24 if (xhr.status == 200)
25 alert(xhr.responseText);
26 else
27 console.log('Error', xhr.statusText);
28 }
29 };
30
31 xhr.sendAsBinary(body);
32
33
34
35 }

上面代码的第18行filename直接用的原文件名称,并没有改名,这在原名为汉字的情况下会提示错误,应该处理一下,但不知道怎么弄

 

描述
application/x-www-form-urlencoded 在发送前编码所有字符(默认)
multipart/form-data

不对字符编码。

在使用包含文件上传控件的表单时,必须使用该值。

text/plain 空格转换为 "+" 加号,但不对特殊字符编码。


上面是在w3cschool搜到的,既然multipart/form-data不对字符编码,但为什么会出错,希望知道的能告诉一声
模拟出来的数据要以二进制发送。火狐XMLHttpRequest 对象中有sendAsBinary()方法,这个是火狐私有的。chrome中没有,但是可以模拟sendAsBinary

1 XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
2 function byteValue(x) {
3 return x.charCodeAt(0) & 0xff;
4 }
5 var ords = Array.prototype.map.call(datastr, byteValue);
6 var ui8a = new Uint8Array(ords);
7 this.send(ui8a.buffer);
8 }

服务端接收文件时和普通上传文件时一样
例如php代码:

if(!empty($_FILES)){
$file=$_FILES['new_image']; //new_image 就是上面的fileFieldName
echo $file['name'];
move_uploaded_file($file['tmp_name'],"./zf/".$file['name']);
die();
}

 

 4.显示上传进度

 1 var xhr = new XMLHttpRequest();
2 upload =xhr.upload;
3 upload.addEventListener("progress", updateProgress, false);//updateProgress 处理上传进度
4 xhr.open('post', “服务端url”, true);
5
6 function updateProgress(evt) {
7 if (evt.lengthComputable) {
8 var percentComplete = Math.round((evt.loaded * 100) / evt.total)
9 bug(percentComplete);
10
11 }
12 else {
13 // Unable to compute progress information since the total size is unknown
14 }
15 }

 

这个也挺简单,代码如上,不过要注意的是:

1.事件要绑定到 XMLHttpRequest的 upload上,这样才能监听上传进度。 XMLHttpRequest上同时也有progress事件,不过是监听下载的。
2. 火狐和chrome调用updateProgress频率好像一样,具体是每秒调用一次还是怎么着,我也不是很清楚。假如文件很小,而且网络很快(本地测试的,当然快)FF就不会触发progress事件。
   在火狐下, 在上传完成时也不触发事件所以总是导致进度到不了100%,到了百分之80,90就不动了。

 

好了,功能基本都实现了,另外上面那些代码都是我在写篇文章是复制过来的(写了将近一个星期的时间,有空就写,程序基本功能完成是在两个星期前。)
最后在附上一个我搜索资料过程中找到的一个jquery 插件 jquery-filedrop

源码下载

posted @ 2011-12-01 18:11 倪浩 阅读(4975) 评论(20) 编辑

2011年8月18日

以前网站要实现了一个在线预览文件的功能,其中后台管理时用到了ajax上传。我用的是一个jquery插件ajaxfileupload.js
今天同事使用时突然报错了(以前没有问题,我们用的都是火狐浏览器,后来我让她用其他浏览器没有问题),我试了一下也出错了。alertSyntaxError: missing ; before statementerror 这个应该是js语法错误。记得当时也出现过这个错误,不过后来怎么解决的忘记了(当然也可能是我记错了可能)。
调用ajaxfileupload上传的部分代码如下

 1 $.ajaxFileUpload
2 (
3 {
4 url:'eb_price.php?'+parm,
5 secureuri:false,
6 fileElementId:'f_file',
7 dataType: 'json',
8 success: function (data, status)
9 {
10 fun(data)
11 },
12 error: function (data,status,e)
13 {
14 alert(e+status)// 商品;
15
16 }
17 }
18 );

经过分析是在调用jQuery.uploadHttpData( xml, s.dataType ); 由于语法错误抛出了异常

View Code
 1  try {
2 status = isTimeout != "timeout" ? "success" : "error";
3 // Make sure that the request was successful or notmodified
4 if ( status != "error" )
5 {
6 // process the data (runs the xml through httpData regardless of callback)
7 var data = jQuery.uploadHttpData( xml, s.dataType );
8 // If a local callback was specified, fire it and pass it the data
9 if ( s.success )
10 s.success( data, status );
11
12 // Fire the global callback
13 if( s.global )
14 jQuery.event.trigger( "ajaxSuccess", [xml, s] );
15 } else
16 jQuery.handleError(s, xml, status);
17 } catch(e)
18 {
19 status = "error";
20 jQuery.handleError(s, xml, status, e);
21 }

由于要返回的数据类型是dataType:'json'格式的;感觉错误出在json字符串的解析上面。即把json格式的字符转换为对象;

 uploadHttpData: function( r, type ) {
var data = !type;
bug(r);
//bug为封装的firebug的console.log();
data = type == "xml" || data ? r.responseXML : r.responseText;
bug(data);
// If the type is "script", eval it in global context
if ( type == "script" )
jQuery.globalEval( data );
// Get the JavaScript object, if JSON is used.
if ( type == "json" )
eval(
"data = " + data );
// evaluate scripts within html
if ( type == "html" )
jQuery(
"<div>").html(data).evalScripts();

return data;
}

两次输出的结果如图


可以看到 第二次bug(data);data的内容多出了一段html代码,都这样了不错才怪呢。这段代码我看着很熟悉,突然想到了。好像是火狐的魔镜插件,嵌入的;

在firebug的网络面板中,可以看到返回的数据貌似是正常的

只不过后来又遭到了修改。
你随便打开一个网页在firebug中可以看到这个隐藏的div;

现在一切都清楚了,
我同事之所以以前用火狐没事,现在突然出现了这个问题,,就是因为两天前 火狐魔镜升级了。(貌似你以前没有安装魔镜,在这次更新过程中,会自动安装。此前把魔镜禁用了,升级为火狐应用中心后,它就成了启用状态了。这只是我的猜想)。
另外这个插件肯定不会在每次请求中都会嵌入代码;这段代码跟播放视频的播放器有关系,通过它在播放器周围显示一个框框,提示你可以单独拿出来播放。
插件之所以会在ajax上传后返回的结果中嵌入,是因为ajaxfileupload中的ajax并不是我们平时所用的那个ajax。它只是创建了一个隐藏的iframe,并在里面创建了表单文件域的等,把文件提交到服务器。然后将iframe中的内容,即web服务器返回的结果,经过处理返回给调用它的代码。

解决方法:

1.换浏览器。(因为这个功能只是我们的后台人员在用,当然可以这么做^_^)

2.卸载插件。

3.js判断是否存在此代码。若存在替换为空字符串;

不过最好的方式还是这个插件不要修改原来网页的结构。感觉在实现原来效果的情况下,而不修改结构是可以实现的。

2011 8.19 昨晚回到家后去火狐社区逛了一圈。遇到这个问题的不光是我一个。会在fckeditor编辑器里插入那个用于弹出视频的代码 
并有回复:问题已经修复已发布5.0.1版 – 超 频
posted @ 2011-08-18 18:01 倪浩 阅读(1373) 评论(5) 编辑

2011年8月2日

摘要: 前些时间修改网站首页,首页上有很多地方用到这种方式来展现内容(不一定非得是图片)。虽然显示效果不同,但还是有相同的地方的。于是就自己写了一个插件(别问我为么不在网上搜个插件)下面是效果图,一共有3张切换效果,普通,淡入淡出,滑动源码网页代码<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w阅读全文
posted @ 2011-08-02 16:27 倪浩 阅读(1453) 评论(6) 编辑

2011年7月16日

摘要: 前些天,要实现一个功能。在后台,一些信息是要实时提醒给后台管理员。用的是通过ajax获取最新信息,然后在前台提示;原来系统自带的是弹出一个对话框,然后flash播放铃声;这种方式不是很友好,对话框弹出一会就消失了,这个页面不能最小化,不然是看不到的;另外播放铃声也得要求管理员带上耳机。于是就想到了邮箱中来新邮件那种提示方式,闪动标题栏;思路是:通过ajax访问后台,若有新消息,则将网页的title替换为 提示信息 ,并与空格来回切换;例:【你有新消息】与【 】切换;提示内容弄是动态的,所以替换文字的空格数目也是算出的。这里用全角的空格;但是如果提示消息中有‘数字’等半角字符的话就会出现问题。.阅读全文
posted @ 2011-07-16 17:27 倪浩 阅读(2884) 评论(15) 编辑

2011年6月1日

据说ecshop的模板类是修改的smarty,不过个人感觉不是修改是完全重写了。它和smarty上只是模板标签上有相同的地方,同时阉割了很多功能。

比如$smarty.const.'常量',这个就不能用。

其实模板引擎原理上并不复杂,只是把一些模板标签替换为php中的函数,变量,语法结构罢了。

这次要在ecshop模板中加入引用常量的功能,只需在函数make_var()中加入两行代码

1 function make_var($val)
2 {
3 if (strrpos($val, '.') === false)
4 {
5 if (isset($this->_var[$val]) && isset($this->_patchstack[$val]))
6 {
7 $val = $this->_patchstack[$val];
8 }
9 $p = '$this->_var[\'' . $val . '\']';
10 }
11 else
12 {
13 $t = explode('.', $val);
14 $_var_name = array_shift($t);
15 if (isset($this->_var[$_var_name]) && isset($this->_patchstack[$_var_name]))
16 {
17 $_var_name = $this->_patchstack[$_var_name];
18 }
19 if ($_var_name == 'smarty')
20 {
21 if($t[0] == 'const'){
22 return strtoupper($t[1]);
23
}
24 $p = $this->_compile_smarty_ref($t);
25 }
26 else
27 {
28 $p = '$this->_var[\'' . $_var_name . '\']';
29 }
30 foreach ($t AS $val)
31 {
32 $p.= '[\'' . $val . '\']';
33 }
34 }
35
36 return $p;
37 }

其中21-23行是新加的,这让就可在模板文件中通过 {$smarty.const.常量}来引用php中定义的常量了

posted @ 2011-06-01 13:30 倪浩 阅读(134) 评论(0) 编辑

2011年3月22日

摘要: setTimeout(function (){test('dd')},5000);function test(a){ alert(a); }test() 五秒后执行阅读全文
posted @ 2011-03-22 15:45 倪浩 阅读(40) 评论(0) 编辑

2011年3月11日

摘要: <div id='test' >fdsfsdf123</div>如 $('#test1').fadeIn(1000) test 中的英文字符在动画效果结束前会加粗解决方法: 为test层设上背景颜色阅读全文
posted @ 2011-03-11 10:46 倪浩 阅读(50) 评论(0) 编辑
摘要: 解决方法:为使用offset的对象的top,left设上值.(另外需要注意的是offset()只对可见元素生效)例如:<div id='test'style="position:absolute;top:0px;left:0px;width:100px; height:100px;" > test</div>$('#test').offset({'top':100,'left':100});阅读全文
posted @ 2011-03-11 10:42 倪浩 阅读(190) 评论(0) 编辑

2011年3月4日

摘要: 写的一个简单的jquery插件,在IE6.0下,自动把div 的position 属性改为absolute。jQuery.fn.extend({ juzhong:function(){ var p={};//在ie浏览器下用top left 和标签相同的做变量,会出问题 h=$(this).height(); w=$(this).width(); p.top =($(window).height()-h)/2; p.left=($(window).width()-w)/2; if($.browser.msie && $.browser.version=='6.0'阅读全文
posted @ 2011-03-04 17:38 倪浩 阅读(177) 评论(0) 编辑

公告

导航

统计