简介:

通过jquery的ajax异步上传文件,使用FormData 对象提交数据,使用xhr显示上传进度条。
主要用于:批量上传图片,上传文件数据,也可用于异步提交数据,而独立于表单使用。
这篇文章主要讲述,批量上传txt的文件,图片类似。

  1. html的使用要求,from必须有属性:enctype=“multipart/form-data” ,如果批量多个文件上传必须有:multiple=“multiple”,accept=“text/plain” 或 accept=“image/*”
<form action='sc.php' method="post" enctype="multipart/form-data" >
	<input type="file" name="ycfile" id="ycfile"  multiple="multiple" accept="text/plain"/>
	<input type="button" value="上传" id="shangchuan"/>
	<div style="width:600px;height:auto;border:1px solid red;" id="zhanshi">
	</div>
</form>

2.导入jquery库,写一个input的change事件,申明一个全局变量i,每张上传文件对应一个数字,一个全局变量data,格式为formdata对象,使用formdata提交数据。
此处可以使用append追加数据,但是append我不知道怎么删除列表中的单独一个元素

var i=0;
		var data = new FormData();
		$("#ycfile").change(function(){
			var files = $(this).prop('files');
			$.each(files,function(index,value){
			 // 使用formdata的set函数,对上传的文件进行保存
				data.set('myfile_'+i, value);
				var html = '<div id="hezi_'+i+'"><span id="biaoti_'+i+'">'+value.name+'</span><strong id="percent_'+i+'">0%</strong><div id="progressnumber_'+i+'" style="background:#428bca;width:0px;height:10px" ></div><span id="shanchu_'+i+'" class="progresssc" scid="'+i+'">删除</span></div>';
				i = i+1;
				//在此处可以做上传图片预览,因为我上传的是txt文件,所以只是展示,做一个删除功能
				$("#zhanshi").append(html);
			
			})
		});

3.点击上传按钮,对每个formdata中的对象逐个异步上传,注意点击按钮直接提交表单。

formdata知识点:https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/values

$("#shangchuan").click(function(){
	if (!data) {
		alert("请上传文件");
		return false;
	}
	var upload_data = data;
	//点击上传就清空data数据
	data = new FormData();
	for (var key of upload_data.keys()) {
		if(key && upload_data.get(key)) {
			shangchuan(key,upload_data.get(key))
		}
	}
});

4.异步进行上传文件数据,然后显示进度条

function shangchuan(key,value)
{
	//截图key后面的数字
	var index = key.split("_").pop();
	var ext = value.name.split(".").pop();
	console.log(key+"来了");
	//console.log(value);
	if(ext !== "txt") {
		$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:上传的文件类型不正确</span>");
		return key;
	}
	if(value.type !== "text/plain") {
		$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:上传的文件类型不正确</span>");
		return key;
	}
	if ((value.size/1024)>(10*1024)) {
		$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:上传的文件大小为10M</span>");
		return key;
	}
	var formData = new FormData();
	formData.append("wenjian",value)
	$.ajax({
		url: 'sc.php',
		type: 'POST',
		data: formData,
		cache: false,
		processData: false,
		contentType: false,
		dataType:'json',
		xhr: function () {
			var xhr = new XMLHttpRequest();
			xhr.upload.addEventListener('progress', function (e) {
				//外部资源加载过程中触发,默认500毫秒。
				//loaded代表上传了多少
				//total代表总数为多少
				var progressRate = Math.round((e.loaded / e.total) * 100,-2) + '%';

				//console.log(progressRate);
				//通过设置进度条的宽度达到效果
				$("#percent_"+index).html(progressRate)
				$("#progressnumber_"+index).css("width",progressRate);
			})
			xhr.upload.addEventListener('load', function (e) {
				//外部资源加载成功时触发。如果后台处理时间过长,可以在此提示“后台正在处理请稍等”
			})
			return xhr;
		},
		success: function (res) {
			if(res.status=='fail') {
				console.log("失败了");
				$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:"+ res.msg +"</span>");
			}else if(res.status=='success'){
				$("#shanchu_"+index).remove();
				$("#hezi_"+index).children('.shibai_tishi').remove();
				console.log(res.data);
				//可以把返回的上传路径展示在页面中
			}else {
				$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:未知原因</span>");
			}
			return key;
		}
	});
}

5.对上传失败的文件进行删除,因为展示代码是jquery追加的,所以需要用on绑定事件
此处大家可以考虑一下,怎么使用FormData.delete(),删除append 追加的指定任意元素

$("#zhanshi").on("click",".progresssc",function () {
	//删除准备上传的文件
	var id = $(this).attr("scid")
	data.delete("myfile_"+id);
	$(this).parent("div").remove();
});

6.php代码处理上传的文件,返回json格式的数据
此处有个问题,就是上传的文件超过php的max_upload设置,直接报错了,这样ajax就没法判返回结果了,要不然再处理结果的时候写个else ,我屏蔽以后没效果,不知道是不是我本地环境的问题.

exit(json_encode(['status'=>'fail',"msg"=>"后缀不正确","data"=>""]));
 exit(json_encode(['status'=>'success',"msg"=>"成功","data"=>trim($upload_path.$file_name)]));

7.全部源码

<!DOCTYPE html>
<head>
	<meta charset=utf-8><meta name=referrer content=always><meta http-equiv=x-dns-prefetch-control content=on><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
	<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
	<title>上传</title>
</head>
<body>
<form action='sc.php' method="post" enctype="multipart/form-data" >
	<input type="file" name="ycfile" id="ycfile"  multiple="multiple" accept="text/plain"/>
	<input type="button" value="上传" id="shangchuan"/>
	<div style="width:600px;height:auto;border:1px solid red;" id="zhanshi">
	</div>
</form>
<script>
	$(function(){
		var i=0;
		var data = new FormData();
		$("#ycfile").change(function(){
			var files = $(this).prop('files');
			$.each(files,function(index,value){
				data.set('myfile_'+i, value);
				var html = '<div id="hezi_'+i+'"><span id="biaoti_'+i+'">'+value.name+'</span><strong id="percent_'+i+'">0%</strong><div id="progressnumber_'+i+'" style="background:#428bca;width:0px;height:10px" ></div><span id="shanchu_'+i+'" class="progresssc" scid="'+i+'">删除</span></div>';
				i = i+1;
				$("#zhanshi").append(html);

			})
		});

		$("#zhanshi").on("click",".progresssc",function () {
			//删除准备上传的文件
			var id = $(this).attr("scid")
			data.delete("myfile_"+id);
			$(this).parent("div").remove();
		});

		function shangchuan(key,value)
		{
			//截图key后面的数字
			var index = key.split("_").pop();
			var ext = value.name.split(".").pop();
			console.log(key+"来了");
			//console.log(value);
			if(ext !== "txt") {
				$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:上传的文件类型不正确</span>");
				return key;
			}
			if(value.type !== "text/plain") {
				$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:上传的文件类型不正确</span>");
				return key;
			}
			if ((value.size/1024)>(10*1024)) {
				$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:上传的文件大小为10M</span>");
				return key;
			}
			var formData = new FormData();
			formData.append("wenjian",value)
			$.ajax({
				url: 'sc.php',
				type: 'POST',
				data: formData,
				cache: false,
				processData: false,
				contentType: false,
				dataType:'json',
				xhr: function () {
					var xhr = new XMLHttpRequest();
					xhr.upload.addEventListener('progress', function (e) {
						//外部资源加载过程中触发,默认500毫秒。
						//loaded代表上传了多少
						//total代表总数为多少
						var progressRate = Math.round((e.loaded / e.total) * 100,-2) + '%';

						//console.log(progressRate);
						//通过设置进度条的宽度达到效果
						$("#percent_"+index).html(progressRate)
						$("#progressnumber_"+index).css("width",progressRate);
					})
					xhr.upload.addEventListener('load', function (e) {
						//外部资源加载成功时触发。如果后台处理时间过长,可以在此提示“后台正在处理请稍等”
					})
					return xhr;
				},
				success: function (res) {
					if(res.status=='fail') {
						console.log("失败了");
						$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:"+ res.msg +"</span>");
					}else if(res.status=='success'){
						$("#shanchu_"+index).remove();
						$("#hezi_"+index).children('.shibai_tishi').remove();
						console.log(res.data);
						//可以把返回的上传路径展示在页面中
					}else {
						$("#hezi_"+index).append("<span class='shibai_tishi'>上传失败:未知原因</span>");
					}
					return key;
				}
			});
		}
		$("#shangchuan").click(function(){
			if (!data) {
				alert("请上传文件");
				return false;
			}
			var upload_data = data;
			//点击上传就清空data数据
			data = new FormData();
			for (var key of upload_data.keys()) {
				if(key && upload_data.get(key)) {
					shangchuan(key,upload_data.get(key))
				}
			}
		});
	})
</script>
</body>
</html>
<?php

ini_set('display_errors', 0);
error_reporting(E_ALL^E_NOTICE^E_WARNING);
$field = "wenjian";
if(!isset($_FILES[$field])) {
    exit(json_encode(['status'=>'fail',"msg"=>"请上传文件","data"=>""]));
}

if ($_FILES[$field]['tmp_name'])
{
    $error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
    switch($error)
    {
        case 1:	// UPLOAD_ERR_INI_SIZE
            exit(json_encode(['status'=>'fail',"msg"=>"上传的文件超过限制","data"=>""]));
            break;
        case 2: // UPLOAD_ERR_FORM_SIZE
            exit(json_encode(['status'=>'fail',"msg"=>"上传的文件超过表单限制","data"=>""]));
            break;
        case 3: // UPLOAD_ERR_PARTIAL
            exit(json_encode(['status'=>'fail',"msg"=>"对不起,文件上传不完整!","data"=>""]));
            break;
        case 4: // UPLOAD_ERR_NO_FILE
            exit(json_encode(['status'=>'fail',"msg"=>"请选择上传的文件!","data"=>""]));
            break;
        case 6: // UPLOAD_ERR_NO_TMP_DIR
            exit(json_encode(['status'=>'fail',"msg"=>"文件上传没有临时目录!","data"=>""]));
            break;
        case 7: // UPLOAD_ERR_CANT_WRITE
            exit(json_encode(['status'=>'fail',"msg"=>"对不起,文件写入失败!","data"=>""]));
            break;
        default :
            break;
    }


    $ext = explode('.', $_FILES[$field]['name']);
    $ext = end($ext);
    $ext = strtolower($ext);
    if(!in_array($ext,array('txt')))
    {
        exit(json_encode(['status'=>'fail',"msg"=>"后缀不正确","data"=>""]));
    }

    $file_size = round($_FILES[$field]['size']/1024,2); //单位kb
    if($file_size>10240)
    {
        //10M
        exit(json_encode(['status'=>'fail',"msg"=>"文件大小请限制在10M以内","data"=>""]));
    }
    $upload_path = "./upload/";
    $file_name = uniqid().rand(1,99999).time().'.'.$ext;

    //开始上传我们将尝试使用副本()。如果该操作失败 我们将使用函数()。这两个应该之一 在大多数环境中可靠地工作
    if ( ! copy($_FILES[$field]['tmp_name'], $upload_path.$file_name))
    {
        if ( ! move_uploaded_file($_FILES[$field]['tmp_name'], $upload_path.$file_name))
        {
            exit(json_encode(['status'=>'fail',"msg"=>"上传失败了。","data"=>""]));
        }
    }
    //因为我这里读取txt文件内容,不需要保存文件就直接删除了
    //unlink($upload_path.$file_name);
    exit(json_encode(['status'=>'success',"msg"=>"成功","data"=>trim($upload_path.$file_name)]));

}
exit(json_encode(['status'=>'fail',"msg"=>"请上传文件","data"=>""]));
?>