• 每一次开始都给予了希望,希望最后的结果不会让我失望

文件上传插件Plupload使用(带图片预览功能)

脚本编程 小米 3418次浏览 已收录 8个评论

本文会按照两部分进行说明:

第一部分:plupload自带demo

第二部分:自己工作中的实例

先上工作中实例图,无图无真相

文件上传插件Plupload使用(带图片预览功能)

最近在写一个手机端相册图片上传功能,第一想法就是用插件,可是做开发这么长时间了一直没有合适的wap端图片上传插件,前些天看过百度开发的webuploader插件不错,不知道wap端兼容性如何。在看过网友的评论后,感觉webuploader不是很兼容wap端。于是乎各种搜,最后还是找到了pluploader这款插件,特别是看到了官网的这句话的时候,我觉得这个可以了。。。

文件上传插件Plupload使用(带图片预览功能)

声明:

plupload最新版本为2.2.1(未封装mOxie对象),但是本例含有图片预览功能(需mOxie对象支持),所以本例pluploader js版本为1.2.1
点击下载
plupload文件上传是一个一个上传(一次一次的请求后台程序,上传图片)的,而不是批量上传

微信端:plupload在ios系统上一次可以选择多张图片,在Android系统(魅族,其他机型没测试过)一次只能选择一张图片

wap端:在QQ浏览器下可以选择多张图片

Plupload有以下功能和特点:

1、拥有多种上传方式:HTML5、flash、silverlight以及传统的<input type=”file” />。Plupload会自动侦测当前的环境,选择最合适的上传方式,并且会优先使用HTML5的方式。所以你完全不用去操心当前的浏览器支持哪些上传方式,Plupload会自动为你选择最合适的方式。

2、支持以拖拽的方式来选取要上传的文件

3、支持在前端压缩图片,即在图片文件还未上传之前就对它进行压缩

4、可以直接读取原生的文件数据,这样的好处就是例如可以在图片文件还未上传之前就能把它显示在页面上预览

5、支持把大文件切割成小片进行上传,因为有些浏览器对很大的文件比如几G的一些文件无法上传。

Plupload的使用方法也与SWFUpload非常类似,可以分为以下几步:

1、引入js文件,plupload的源文件可以到github上去下载

2、实例化一个plupload对象,传入一个配置参数对象进行各方面的配置。

3、调用plupload实例对象的init()方法进行初始化

4、在plupload实例对象上注册各种你需要的事件。plupload从选取文件到文件上传完成这个过程中,会触发很多事件。我们可以通过这些事件来跟plupload进行交互。

5、实现你自己所注册的那些事件的监听函数,利用这些监听函数来进行更新UI、提示上传进度等工作。

第一部分

首先看一下demo文件目录和效果图

文件上传插件Plupload使用(带图片预览功能)

文件上传插件Plupload使用(带图片预览功能)

 

(一)实例化uploader对象并设置监听事件

//实例化一个plupload上传对象
var uploader = new plupload.Uploader({
    runtimes: 'html5,flash,silverlight,html4',
    browse_button: 'pickfiles',
    // you can pass an id...
    container: document.getElementById('container'),
    // ... or DOM Element itself
    url: 'up.php',
    flash_swf_url: '../js/Moxie.swf',
    silverlight_xap_url: '../js/Moxie.xap',
    multi_selection: true,
    //是否可以在文件浏览对话框中选择多个文件,true为可以,false为不可以
    unique_names: true,
    filters: {
        max_file_size: '10mb',
        mime_types: [{
            title: "Image files",
            extensions: "jpg,gif,png"
        },
        {
            title: "Zip files",
            extensions: "zip"
        }],
        prevent_duplicates: true //不允许选取重复文件
    },
    init: {
        PostInit: function() {
            document.getElementById('filelist').innerHTML = '';

            document.getElementById('uploadfiles').onclick = function() {
                uploader.start();
                return false;
            };
        },

        FilesAdded: function(up, files) {
            plupload.each(files,
            function(file) {
                //document.getElementById('filelist').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></div>';
                var html = '<li id="file-' + file.id + '" delid=' + file.id + '><p class="file-name">' + file.name + '</p><p></p></li>';
                $(html).appendTo('#file-list');
                previewImage(file,
                function(imgsrc) {
                    $('#file-' + file.id).append('<img src="' + imgsrc + '" />');
                })
            });
        },

        UploadProgress: function(up, file) {
            document.getElementById('file-' + file.id).getElementsByTagName('p')[1].innerHTML = '<span>' + file.percent + "%</span>";
        },

        Error: function(up, err) {
            document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));
        }
    }
});

(二)调用pluploader的init()方法

uploader.init();

note:最新版提供的examples例子,FilesAdded,UploadProgress,Error事件监听写在实例化uploader对象里面init:{}方法里面,当然也可以先创建对象(没有init:{}方法)->uploader.init()->绑定监听事件

(一)实例化uploader对象

//实例化一个plupload上传对象
var uploader = new plupload.Uploader({
    runtimes: 'html5,flash,silverlight,html4',
    browse_button: 'pickfiles',
    // you can pass an id...
    container: document.getElementById('container'),
    // ... or DOM Element itself
    url: 'up.php',
    flash_swf_url: '../js/Moxie.swf',
    silverlight_xap_url: '../js/Moxie.xap',
    multi_selection: true,
    //是否可以在文件浏览对话框中选择多个文件,true为可以,false为不可以
    unique_names: true,
    filters: {
        max_file_size: '10mb',
        mime_types: [{
            title: "Image files",
            extensions: "jpg,gif,png"
        },
        {
            title: "Zip files",
            extensions: "zip"
        }],
        prevent_duplicates: true //不允许选取重复文件
    }
});

(二)uploader对象init()

uploader.init();

(三)uploader设置监听事件

uploader.bind('PostInit',
function(uploader, files) {
    document.getElementById('filelist').innerHTML = '';
    document.getElementById('uploadfiles').onclick = function() {
        uploader.start();
        return false;
    };
});
uploader.bind('FilesAdded',
function(up, files) {
    plupload.each(files,
    function(file) {
        var html = '<li id="file-' + file.id + '" delid=' + file.id + '><p class="file-name">' + file.name + '</p><p></p></li>';
        $(html).appendTo('#file-list');
        previewImage(file,
        function(imgsrc) {
            $('#file-' + file.id).append('<img src="' + imgsrc + '" />');
        })
    });
});
uploader.bind('UploadProgress',
function(uploader, files) {
    document.getElementById('file-' + file.id).getElementsByTagName('p')[1].innerHTML = '<span>' + file.percent + "%</span>";
});
uploader.bind('Error',
function(uploader, files) {
    document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));
});

图片预览代码写在FilesAdded监听事件里面

FilesAdded: function(up, files) {
    plupload.each(files,
    function(file) {
        //document.getElementById('filelist').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></div>';
        var html = '<li id="file-' + file.id + '" delid=' + file.id + '><p class="file-name">' + file.name + '</p><p></p></li>';
        $(html).appendTo('#file-list');
        previewImage(file,
        function(imgsrc) {
            $('#file-' + file.id).append('<img src="' + imgsrc + '" />');
        })
    });
},

其中previewImage(file,function(imgsrc){$(‘#file-‘+file.id).append(‘<img src=”‘+ imgsrc +'” />’);)方法如下

//plupload(1.2)中为我们提供了mOxie对象
//有关mOxie的介绍和说明请看:https://github.com/moxiecode/moxie/wiki/API
function previewImage(file, callback) { //file为plupload事件监听函数参数中的file对象,callback为预览图片准备完成的回调函数
    if (!file || !/image\//.test(file.type)) return;
    if (file.type == 'image/gif') { //gif使用FileReader进行预览,因为mOxie.Image只支持jpg和png
        var fr = new mOxie.FileReader();
        fr.onload = function() {
            callback(fr.result);
            fr.destroy();
            fr = null;
        }
        fr.readAsDataURL(file.getSource());
    } else {
        var preloader = new mOxie.Image();
        preloader.onload = function() {
            preloader.downsize(300, 300); //先压缩一下要预览的图片,宽300,高300
            var imgsrc = preloader.type == 'image/jpeg' ? preloader.getAsDataURL('image/jpeg', 80) : preloader.getAsDataURL(); //得到图片src,实质为一个base64编码的数据
            callback && callback(imgsrc); //callback传入的参数为预览图片的url
            preloader.destroy();
            preloader = null;
        };
        preloader.load(file.getSource());
    }
}

因为图片预览previewImage方法用到了mOxie对象,而plupload 1.2版本提供了mOxie对象,最新版本2.2.1貌似没有此对象

以下jquery部分代码为点击预览图片删除图片

$(function() {
    $('#file-list').on('click', 'li img',
    function() {
        var toremove = '';
        var delid = $(this).parent('li').attr("delid");
        $(this).parent('li').remove();
        for (var i in uploader.files) {
            if (uploader.files[i].id === delid) {
                toremove = i;
            }
        }
        uploader.files.splice(toremove, 1);
    })
})

除了dom元素需要remove掉添加显示的预览图片外,还需要从上传队列中移除先前添加的文件

for(var i in uploader.files){
	if(uploader.files[i].id === delid){
	toremove = i;
   }
}
uploader.files.splice(toremove, 1);

最后贴上简单的后端处理程序

<?php
if(empty($_FILES) || $_FILES["file"]["error"]) {
  die('{"OK": 0}');
}
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_POST['name']."");
$fileName = $_FILES["file"]["name"];
$ext = end(explode('.', $fileName));
$desFileName = md5(uniqid()).'.'.$ext;
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/$desFileName");
die('{"OK": 1}');
?>

 

使用Plupload的关键是了解它众多的配置参数、事件以及属性和方法。中文翻译的可以在网上搜索到。

 

第二部分

客户需求:买家下完订单付款后,可以上传图片,然后卖家可以将用户上传的图片进行打印,因为一个订单可以有1种或者多种产品,因此就需要实例化多个uploader对象,而功能的难点就是实例化多个uploader对象后,上传队列文件移除,上传文件数量控制(因为每一款产品允许上传的图片数量是后台可以设置的)

先看一下需要实例化多个uploader对象的图片

文件上传插件Plupload使用(带图片预览功能)

下面是核心代码

{loop $goods $g}
	{php $total = $g['picuploadednum']*$g['total']}
	<div class="fui-list goods-list" data-goodsid="{$g['goodsid']}">
		<a class="fui-list-media" href="{php echo mobileUrl('goods/detail',array('id'=>$g['goodsid']))}">
			<img src="{php echo tomedia($g['thumb'])}" class="round">
		</a>
		<a class="fui-list-inner" href="{php echo mobileUrl('goods/detail',array('id'=>$g['goodsid']))}">
			<div class="text goodstitle">{$g['title']}</div>
			{if !empty($g['optionid'])}<div class='subtitle'>{$g['optiontitle']}</div>{/if}
		</a>
		<div class='fui-list-angle'>
			<p>¥<span class='marketprice'>{$g['price']}<br/>x{$g['total']}</span></p>
		</div>

	</div>
	{if $g['picuploadedstatus']==1}
	<div class="fui-title">
		最多可上传<span style='color:red;'>{$total}</span>张图片
		{if !empty($g['picuploadedbeizhu'])}
		<div class='fui-cell'>
			<div class="" style='color:red;'>{$g['picuploadedbeizhu']}</div>
		</div>
		{/if}
	</div>
	<div class="fui-cell-group goods-comment-cell" style='display:block;'>
		<div class='fui-cell' style='display:none;'>
			<div class='fui-cell-label'>传图</div>
			<div class='fui-cell-info'>

				<ul class="fui-images fui-images-sm"  id='fui-images{$g['id']}'></ul>
				<div class="fui-uploader fui-uploader-sm" data-orderid="{$order['id']}"
					 data-max="{$total}"
					 data-count="0"  id="fui-images-max{$g['id']}">
					<input type="file" name='imgFile{$g['id']}' id='imgFile{$g['id']}' multiple="" accept="image/*" >
				</div>
			</div>
		</div>
		
		<!--plupload start-->
		<div id="filelist{$g['id']}" class='flst'></div>
		<br />
		<div class="clear" style="clear:both;"></div>
		<div id="{$g['id']}" class="pluploadcls">
			<a id="pickfiles{$g['id']}" href="javascript:;">添 加</a> 
			<a id="uploadfiles{$g['id']}" href="javascript:;">上 传</a>
		</div>
		<div class="fui-title">
			<div class="fui-cell">
				<div class="" style="color: #f90;">Android系统上传图片时,请复制当前链接到QQ浏览器进行图片批量上传操作</div>
			</div>
		</div>
		<br />
		<pre id="console{$g['id']}"></pre>
		<!--plupload end-->
		
		<div class='fui-cell'>
			<div class='fui-cell-label'>备注</div>
			<div class='fui-cell-info'><textarea rows="3" placeholder="如有备注请注明"></textarea></div>
		</div>
	</div>
	{else}
	
	<div class="fui-title">此产品禁止上传图片</div>
	{/if}
{/loop}

plupload js代码如下

<script type="text/javascript">
var uploaders = new Array();
initUploaders = function(uploaders) {
$('.pluploadcls').each(function(){
	var nowid = $(this).attr('id');
	var btn  = 'pickfiles'+nowid;
	var max = $('#fui-images-max'+nowid).data('max');//允许上传的最大文件数量
	var uploader = new plupload.Uploader({
		runtimes : 'html5,flash,silverlight,html4',
		browse_button : btn, // you can pass an id...
		//container: document.getElementById('container'), // ... or DOM Element itself
		url : "{php echo mobileUrl('util/uploader')}&orderid={$order['id']}",
		flash_swf_url : '../addons/ewei_shopv2/static/plupload/Moxie.swf',
		silverlight_xap_url : '../addons/ewei_shopv2/static/plupload/Moxie.xap',
		filters : {
			max_file_size : '10mb',
			mime_types: [
				{title : "Image files", extensions : "jpg,jpeg,gif,png"},
				{title : "Zip files", extensions : "zip"}
			]
		},
		init: {
			//当Init事件发生后触发
			PostInit: function() {
				document.getElementById('filelist'+nowid).innerHTML = '';
				document.getElementById('uploadfiles'+nowid).onclick = function() {
					uploader.start();
					return false;
				};
			},
		//当文件添加到上传队列后触发
		FilesAdded: function(up, files) {
			 var filelistNum = $('#filelist'+nowid).children('div').length;
			 if(uploader.files.length>max){ // 最多上传max张图
		            uploader.splice(filelistNum,999);
		            alert('已超出图片上传数量');
		            return false;
		        }
			plupload.each(files, function(file) {
				document.getElementById('filelist'+nowid).innerHTML += '<div id="' + file.id 
				+ '">' + plupload.formatSize(file.size) + '<b class="remove" data-val='+file.id+'>删除</b></div>';
				previewImage(file,function(imgsrc){
					$('#'+file.id).append('<br/><img src="'+ imgsrc +'" />');
				});
					
			});
			$('#filelist'+nowid+" b").on('click',function() {
				var delid =  $(this).data('val');
			      //uploader.removeFile(file);
				  for(var i in uploader.files){
					if(uploader.files[i].id === delid){
						toremove = i;
					     }
					}
				    uploader.files.splice(toremove, 1); 
			      $(this).parent().remove();
			 });
			},
		//会在<a href="http://www.miduoyu.com/tag/%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0" title="查看更多关于文件上传的文章" target="_blank">文件上传</a>过程中不断触发,可以用此事件来显示上传进度
		UploadProgress:function(up, file) {
				document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '<span>' + 
				file.percent + "%"+"&nbsp;&nbsp;&nbsp;删除</span>";
			},
		//当队列中的某一个<a href="http://www.miduoyu.com/tag/%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0" title="查看更多关于文件上传的文章" target="_blank">文件上传</a>完成后触发
		FileUploaded:function(up,file,res){
    	                var res  = $.parseJSON(res.response);
			if(res.status == 'success'){
                        	//上传完成的图片移除 删除<b>按钮
                        	$("#"+file.id).find('b').remove();
                        	var html = document.getElementById("fui-images"+nowid).innerHTML;
                        	document.getElementById("fui-images"+nowid).innerHTML = html + '<li data-filename="'+res.filename+'"></li>';
                        	//用来判断用户是否上传图片,如果未上传图片,提交时会提示
                        	var already  = $('#uploadedalready').data('num');
                        	$('#uploadedalready').data('num',already+1);
                        }else if(res.status =='error'){
                        	alert('当前图片上传失败,请重新上传');
                        	$('#'+file.id).remove();
                        	return;
                        }
		},
		Error: function(up, err){
			document.getElementById('console'+nowid).appendChild(document.createTextNode("\nError #" + err.code +
			 ": " + err.message));
	            }
		}
	});
	uploader.init();
	uploaders.push(uploader);
});//each end
};//initUploaders end
initUploaders(uploaders);
	function previewImage(file,callback){//file为plupload事件监听函数参数中的file对象,callback为预览图片准备完成的回调函数
		if(!file || !/image\//.test(file.type)) return; 
			if(file.type=='image/gif'){//gif使用FileReader进行预览,因为mOxie.Image只支持jpg和png
				var fr = new mOxie.FileReader();
				fr.onload = function(){
					callback(fr.result);
					fr.destroy();
					fr = null;
				}
				fr.readAsDataURL(file.getSource());
			}else{
				var preloader = new mOxie.Image();
				preloader.onload = function() {
				    preloader.downsize( 300, 300 );//先压缩一下要预览的图片,宽300,高300
				    var imgsrc = preloader.type=='image/jpeg' ? preloader.getAsDataURL('image/jpeg',80):preloader.getAsDataURL(); //得到图片src,实质为一个base64编码的数据
				    callback && callback(imgsrc); //callback传入的参数为预览图片的url
				    preloader.destroy();
				    preloader = null;
				};
			preloader.load( file.getSource() );
		}
	}
</script>

1、首先实例化多个uploader对象push到uploaders数组里面

initUploaders(uploaders);

解决了同一个页面需要实例化多个uploader对象的尴尬

2、删除图片

文件上传插件Plupload使用(带图片预览功能)

“删除”按钮是在FilesAdded监听里面添加的,代码为39行

plupload.each(files, function(file) {
   previewImage(file,function(imgsrc){
	$('#'+file.id).append('<br/><img src="'+ imgsrc +'" />');
    });
   document.getElementById('filelist'+nowid).innerHTML += '<div id="' + file.id + '">' + plupload.formatSize(file.size) + 
   '<b class="remove" data-val='+file.id+'>删除</b></div>';
});

其中previewImage(file,function(imgsrc){}函数是将图片以Data URI形式展现在前端页面,以此来实现图片预览功能。

删除图片,不仅仅是通过jquery将图片隐藏掉,而且还需要将file对象从plupload上传队列中移除,否则的话,即便前端jquery隐藏掉图片,还是会上传到服务器

其次如果file没有从plupload上传队列移除的话,就不能很好的控制商品允许的上传量

在uploader js 代码第47行

$('#filelist'+nowid+" b").on('click',function() {
	var delid =  $(this).data('val');
	//uploader.removeFile(file);
	for(var i in uploader.files){
		if(uploader.files[i].id === delid){
			toremove = i;
		   }
		}
	uploader.files.splice(toremove, 1); 
	$(this).parent().hide();
 });

在FilesAdded监听里进行设置,使用uploader.files.splice()或者uploader.removeFile()从队列里移除文件,网上搜索过要是移除队列所有files时uploader.files.splice()比uploader.removeFile()速度快,然后在工作实践中,感觉在逻辑控制方面uploader.files.splice()也比较好用

对上传图片数量进行限制

uploader js代码33行

var filelistNum = $('#filelist'+nowid).children('div').length;
 if(uploader.files.length>max){ // 最多上传max张图
    uploader.splice(filelistNum,999);
    alert('已超出图片上传数量');
    return false;
}

因为每个商品限制用户上传的数量,当用户选择多个图片时,如果图片的数量超过了商品限制的上传数量,那么应该提示用户。而这个判断也是在FilesAdded监听里进行处理:

if(uploader.files.length>max){ // 最多上传max张图
    //ToDo
    alert('已超出图片上传数量');
    return false;
}

其中max是此产品允许的最大上传数量,但是此刻面临的问题是:

  1. 刚才买家选择图片的时候,超出的图片也被添加到队列里面来了,但是前端并没显示(plupload.each(files, function(file){}代码并没执行)
  2. 买家再次选图时,还是会提示“已超出图片上传数量”
  3. 如何将客户所选的这些超出产品允许数量的图片从队列中移除,并且先前已经上传的图片还保留住

这里投机取巧了一把(因为没有找到pluploader获取当前file对象的方法操作)

var filelistNum = $('#filelist'+nowid).children('div').length;

filelisNum是当前页面显示的图片数量,也就是在uploader.files队列里面已经存在的file数量

然后用splice()方法移除超出的数量

uploader.splice(filelistNum,999);

pluploader的其他监听时间UploadProgress,FileUploaded所处理的功能都比较容易了。


米多博客 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明文件上传插件Plupload使用(带图片预览功能)
喜欢 (6)
小米
关于作者:
本人是89年程序员,米多是我儿子的名字,这也是米多博客的来由。博客主要记录自己生活、工作上的点点滴滴。
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(8)个小伙伴在吐槽
  1. ios微信上传预览的时候图片变成横向显示,博主有遇到吗?
    小老孙2017-06-10 22:05 回复 Windows 10 | Chrome 58.0.3029.110
    • 小米
      一直用的android手机,ios微信上传预览还没测试。
      小米2017-06-19 14:48 回复 Windows 7 | Firefox 53.0
  2. 我想问一下 可以传入一张图片 向服务器端发请求 发送三份不同比例的的图片吗
    李逍遥2017-05-11 17:45 回复 Windows 7 | Chrome 58.0.3029.81
    • 小米
      要想获取到三份乃至多份不同比例的图片是在服务器端程序代码处理的。也就是图片裁剪功能,裁剪的尺寸是由程序来处理的,而不是前端图片上传插件。
      小米2017-05-26 08:42 回复 Windows 7 | Firefox 53.0
  3. 安卓微信内支持吗
    CGerAJ2017-03-26 13:43 回复 Windows 10 | Chrome 58.0.3018.3
    • 小米
      支持!! :smile:
      小米2017-03-28 16:15 回复 Windows 7 | Firefox 52.0
  4. 小米
    说实话,兼容手机的上传插件的话,这个真算不错了 :oops:
    大头2017-02-27 12:58 回复 Windows 7 | Chrome 56.0.2924.87
  5. 小米
    温馨提示:工作中部分开发已经在使用plupload插件了
    小米2017-02-27 12:57 回复 Windows 7 | Chrome 56.0.2924.87