手动实现完全加强版js文件上传到php

最近做项目,要用到js上传大文件插件,网上找了很多上传插件,最后试着用了jquery file upload插件,然后确实很简单就可以上传了,demo也可以直接用,但是具体的实现逻辑本封装了,而我木有去看js源码(有点懒,源码太多了),而且php服务器的源码也太大,完全看不过来,于是自己搜了搜ajax文件上传的基本逻辑,结合一些资料,自己鼓捣了一个简单文件上传插件,麻雀虽小,但是五脏俱全。


先说说ajax上传文件的基本逻辑:

首先是选择文件,使用fileinput标签,会得到一个待上传文件的队列:file_list

然后,选择提交(submit)表单,或者把文件队列加到formData里,作为ajax的data部分,文件会先被上传到指定服务器的文件缓存文件夹,存为缓存文件(外部不可访问),缓存文件夹是服务器配置的,例如php是在php.in里面配置缓存文件夹。

一直到前一步,服务器都是默认操作,直到客户端进度条走完,也就是上传完毕,自己写的服务器脚本开始运行,指定的服务器脚本会获取ajax提交的文件信息和缓存的文件路径,这时候,我们就可以在脚本里把缓存文件存到自己文件夹下,存为自定义的文件名,

然后,服务器返回客户端json数据,于是客户端ajax异步调用完成,调用回调函数处理服务器返回数据


1、首先:html基本元素

<div class="container">
<form action="uploadtemp.php" method="post" enctype="multipart/form-data">
    <span class="btn btn-success fileinput-button">
        <i class="glyphicon glyphicon-plus"></i>
        <span>Add files...</span>
        <!-- The file input field used as target for the file upload widget -->
        <input id="fileupload2" type="file" name="file_list[]" multiple>
    </span>
</form>
    <div style="width: 100%;float:left">
        <ul id="upload_file_list"  class="file_list_ul">
            <li class="file_list_li">
                <img class="image_min_preview" src="http://placeholdit.imgix.net/~text?txtsize=50&bg=grey&txt=实例&w=300&h=300">
                <div class="file_list_file_name">I am file name</div>
                <div class="file_list_file_msg">This is a return msg</div>
                <button class="file_list_file_cancel_btn btn btn-primary">This is cancel btn</button>
            </li>
        </ul>
    </div>
    <button id="start_upload_file_list" class="btn btn-primary" >开始上传</button>
    <button id="abort_upload_file_list" class="btn btn-primary" >取消上传</button>
    <div style="margin:10px 0px">
        <div id="file_list_progress" class="_progress">
            <div class="_progress-bar"></div>
        </div>
    </div>
    <div style="width: 100%;float:left">
        <ul id="download_file_list"  class="file_list_ul">
            <li class="file_list_li">
                <img class="image_min_preview" src="http://placeholdit.imgix.net/~text?txtsize=50&bg=grey&txt=实例&w=300&h=300">
                <div class="file_list_file_name">I am download file name</div>
                <div class="file_list_file_msg">This is a return msg</div>
                <button class="file_list_file_cancel_btn btn btn-primary">This is cancel btn</button>
            </li>
        </ul>
    </div>
</div>



因为我用了bootstrap,只是为了界面好看点,必须元素就三个 #fileupload2文件选择控件,然后上传队列预览div,上传按钮,取消上传按钮,进度条,还有下载队列div(上传后由服务器返回)

2、获取上传文件队列

因为我们使用ajax上传,所以使用input file的change事件,每次获取选择的文件,并保存到file_list队列
$("#fileupload2").change(function(){
            var $this=$(this);
            var files=$this[0].files;
            //需要计算上传的数据总量和数量,限制上传大小
            for(index in files){
                //在list中列出所有待上传文件\
                if(!isEmptyObject(files[index])){
                    //添加到上传队列
                    if($.inArray(files[index].name,file_list.file_name_list)!=-1){
                        alert("this file : "+files[index].name+" is already in the file list");
                        continue;
                    }
                    file_list.push(files[index]);
		//该函数用于在上传队列预览div里创建预览条目
                    createFilePreview(files[index], $("#upload_file_list"));
                }
            }
$("#fileupload2").val('');


3、给上传按钮绑定点击事件
点击按钮即实现文件上传:


$("#start_upload_file_list").click(function(){
            //alert("max_upload_file_num:"+max_upload_file_num);
            //alert("max_upload_file_total_size:"+max_upload_file_total_size);
            var $this=$(this);
            $('#file_list_progress .progress-bar').css('width','0%');
            //设置按钮不可用
            $("li.file_list_li button").addClass("disabled").text("上传后不可用");
            //提交上传
            var formdata=new FormData();
            for(index in file_list){
                if(!isEmptyObject(file_list[index])){
                    file_num=file_num+1;
                    file_total_size=file_total_size+file_list[index].size;
                    formdata.append(file_list_name,file_list[index]);
                }
            }
            //p判定是否满足上传限制
            if(file_total_size>max_upload_file_total_size || file_num>max_upload_file_num){
                alert("上传文件数不能超过"+max_upload_file_num+
                        "\r\n"+"上传文件总大小不能超过"+max_upload_file_total_size_M+"M"+
                        "\r\n"+"您的上传文件错处,请重新选择上传队列");
                return;
            }
            $this.button('loading');
            //ajax请求
            var upload_ajax_request=$.ajax({
                url: 'uploadtemp.php',
                type: 'POST',
                data: formdata,
                cache: false,
                processData: false,
                contentType: false,
                //这里我们先拿到jQuery产生的 XMLHttpRequest对象,为其增加 progress 事件绑定,然后再返回交给ajax使用
                xhr: function(){
                    var xhr = $.ajaxSettings.xhr();
                    if(onprogress && xhr.upload) {
                        xhr.upload.addEventListener("progress" , onprogress, false);
                        return xhr;
                    }
                },
                success:function(data,status){
                    //alert(data);
                    var obj;
                    try {
                       obj = JSON.parse(data);
                    }catch (e){
                        alert("error:"+data);
                        //$this.button('上传出错');
                    }
                    //ShowObjProperty(obj);
                    for(file_name in obj){
                        //index
                        if(obj[file_name].success){
                            //上传成功
                            //上传成功从文件上传队列移除
                            $("li[name='upload_"+file_name+"']").slideUp('slow',function(){
                                $("li[name='upload_"+file_name+"']").remove();
                            });
                            createFileUploadSuccessView(obj[file_name],$("#download_file_list"));
                            //从队列里面删除返回成功的file
                            var index=$.inArray(file_name,file_list.file_name_list);
                            if(index!=-1){
                                file_list.file_name_list.splice(index,1);
                                file_list.splice(index,1);
                            }
                            $("li[name='upload_"+file_name+"'] div[name='msg']").html("<a href='"+obj[file_name].url+"'><font color='green'>上传成功</font></a>").fadeIn();
                        }else{
                            //上传失败
                            $("li[name='upload_"+file_name+"'] div[name='msg']").html("<font color='color'>"+obj[file_name].msg+"</font>").fadeIn();
                            $("li[name='upload_"+file_name+"'] button").removeClass("disabled").text("移除");
                        }
                    }
                    if(file_list.length==0){
                        $("#file_list_progress").slideUp();
                        $("#start_upload_file_list").slideUp();
                    }
                    $("#abort_upload_file_list").fadeOut();
                    $this.button('reset');
                    //alert(status);
                },
                error:function(xhr,error,exception){
                    alert(error);
                },
                complete:function(XHR, TS){

                },
                beforeSend:function(xhr){
                }
            });
            //显示 取消上穿 按钮
            $("#abort_upload_file_list").fadeIn().click(function(){
                upload_ajax_request.abort();
                $("#abort_upload_file_list").fadeOut();
                $this.button('reset');
            });


        });
注意,在点击确定上传后,在将上传队列文件加载到formData对象里,再将formData作为数据data上传到服务器端
也可以看到ajax的回调函数可以处理服务器返回值

4、服务器端的实现:

简单地实现php的文件上传就可以了,注意规定好前后台通信的json格式就好了
<?php
/**
 * Created by PhpStorm.
 * User: GongCheng
 * Date: 2017/03/16
 * Time: 03:32 PM
 */
require_once("php/strTools.php");
$file_name='file_list';
if(isset($_FILES[$file_name])) {
    //echo "files2<br/>";
    $count = count($_FILES[$file_name]['name']);
    $return = array();
    for($i=0;$i<$count;$i++){
        $file=array(
            'name'=>$_FILES[$file_name]['name'][$i],
            'type'=>$_FILES[$file_name]['type'][$i],
            'size'=>$_FILES[$file_name]['size'][$i],
            'tmp_name'=>$_FILES[$file_name]['tmp_name'][$i],
            'error'=>$_FILES[$file_name]['error'][$i],
        );
        //var_dump($file);
        $return[$file['name']]=jquery_save_img($file);
        //echo $file['name']." : <br/>";
        //var_dump($return[$file['name']]);
    }
    //echo json_encode($return);
    echo ch_json_encode($return);
    //echo json_last_error();
    //var_dump($return);
    //jquery_save_img($file_name);
}else{
    echo json_encode(array(false=>array('msg'=>'can not find file_list name')));
}
function jquery_save_img($file)
{
    $arrType=array('image/jpg','image/gif','image/png','image/bmp','image/pjpeg','image/jpeg',
        'application/octet-stream','application/x-zip-compressed',
        'video/mp4');
    $max_size='500000000000';      // 最大文件限制(单位:byte)
    $upfile='./upload_files'; //图片目录路径
    //$file=$_FILES[$file_name];
    //if(isset($_FILES["file"])
    /*
    echo 'filename:'.$file['tmp_name'].';<br />';
    echo 'size:'.$file['size'].';<br />';
    echo 'type:'.$file['type'].';<br />';
    echo 'name:'.$file['name'].';<br />';
    */
    if($_SERVER['REQUEST_METHOD']=='POST'){ //判断提交方式是否为POST
        //clearstatcache();
        //$file['tmp_name'] = str_replace('', '//', $file['tmp_name']);
        if(!is_uploaded_file($file['tmp_name'])){ //判断上传文件是否存在
            //echo $file['tmp_name']." -> ".$upfile."/".mb_convert_encoding($file['name'],"gbk", "utf-8");;
            return (array(
                'success'=>false,
                'msg'=>'文件不存在!'
            ));
        }
        //echo "我是:haha";
        if($file['size']>$max_size){  //判断文件大小是否大于500000字节
            return  (array(
                'success'=>false,
                'msg'=>'上传文件太大!'
            ));

        }
        if(!in_array($file['type'],$arrType)){  //判断图片文件的格式
            return  (array(
                'success'=>false,
                'msg'=>'上传文件格式不对!xxx:'.$file['type']
            ));

        }
        if(!file_exists($upfile)){  // 判断存放文件目录是否存在
            mkdir($upfile,0777,true);
        }
        $imageSize=getimagesize($file['tmp_name']);
        $img=$imageSize[0].'*'.$imageSize[1];
        $fname=$file['name'];
        $ftype=explode('.',$fname);
        //因为在windows下为gbk编码,而php默认utf8编码,如果不转换编码,在中文时容易出现编码错误而不能存储
        //mb_convert_encoding函数:转换编码
        //mb_convert_encoding('待转化字符串','想要转换的编码','本身的编码')
        $picName=$upfile."/".mb_convert_encoding($fname,"gbk", "utf-8");
        //echo $picName;

        if(file_exists($picName)){
            return (array(
                'success'=>false,
                'msg'=>'同文件名已存在!'.$fname
            ));

        }
        if(!move_uploaded_file($file['tmp_name'],$picName)){
            return  (array(
                'success'=>false,
                'msg'=>'移动文件出错!'.$picName
            ));
            //echo "<font color='#FF0000'>移动文件出错!</font>";

        }
        else{
            /*
                echo "<font color='#FF0000'>图片文件上传成功!</font><br/>";
                echo "<font color='#0000FF'>图片大小:$img</font><br/>";
                echo "图片预览:<br><div style='border:#F00 1px solid; width:200px;height:200px'>
                <img src=\"".$picName."\" width=200px height=200px>".$fname."</div>";
            */
            return (array(
                'success'=>true,
                    'url'=>$upfile."/".$fname,
                    'name'=>$fname,
                    'size'=>$file['size'],
                    'type'=>$file['type']
                ));
            //echo '{"imgurl":"'.$picName.'"}';
        }
    }

}
?>



最后:开发难点:

1、因为xhr对象已经预置了上传过程中的各种参数,比如上传进度回调函数,所以直接利用xhr对象实现进度条
但是jquery的ajax函数不提供原始的xhr对象,而只有在open函数前设置progress进度函数才起作用,所以无法
直接实现,但是jqeury的ajax函数可以使用自己建立的xhr对象,于是在ajax函数之前创建自己的xhr函数并设置
进度显示函数:onprogress
var upload_ajax_request=$.ajax({
                url: 'uploadtemp.php',
                type: 'POST',
                data: formdata,
                cache: false,
                processData: false,
                contentType: false,
                //这里我们先拿到jQuery产生的 XMLHttpRequest对象,为其增加 progress 事件绑定,然后再返回交给ajax使用
                xhr: function(){
                    var xhr = $.ajaxSettings.xhr();
                    if(onprogress && xhr.upload) {
                        xhr.upload.addEventListener("progress" , onprogress, false);
                        return xhr;
                    }
                },
                success:function(data,status){
                    //alert(data);
                    var obj;
                    try {
                       obj = JSON.parse(data);
                    }catch (e){
                        alert("error:"+data);
                        //$this.button('上传出错');
                    }
                    //ShowObjProperty(obj);
                    for(file_name in obj){
                        //index
                        if(obj[file_name].success){
                            //上传成功
                            //上传成功从文件上传队列移除
                            $("li[name='upload_"+file_name+"']").slideUp('slow',function(){
                                $("li[name='upload_"+file_name+"']").remove();
                            });
                            createFileUploadSuccessView(obj[file_name],$("#download_file_list"));
                            //从队列里面删除返回成功的file
                            var index=$.inArray(file_name,file_list.file_name_list);
                            if(index!=-1){
                                file_list.file_name_list.splice(index,1);
                                file_list.splice(index,1);
                            }
                            $("li[name='upload_"+file_name+"'] div[name='msg']").html("<a href='"+obj[file_name].url+"'><font color='green'>上传成功</font></a>").fadeIn();
                        }else{
                            //上传失败
                            $("li[name='upload_"+file_name+"'] div[name='msg']").html("<font color='color'>"+obj[file_name].msg+"</font>").fadeIn();
                            $("li[name='upload_"+file_name+"'] button").removeClass("disabled").text("移除");
                        }
                    }
                    if(file_list.length==0){
                        $("#file_list_progress").slideUp();
                        $("#start_upload_file_list").slideUp();
                    }
                    $("#abort_upload_file_list").fadeOut();
                    $this.button('reset');
                    //alert(status);
                },
                error:function(xhr,error,exception){
                    alert(error);
                },
                complete:function(XHR, TS){

                },
                beforeSend:function(xhr){
                }
            });

2、通过每次file input对象的每次change事件,记录选择的文件,将其记录到file_list数组,并将file对象的value值置null,不然每次选择同样的文件会不响应change函数
 $("#fileupload2").change(function(){
some scripts;//必须先把处理代码卸了
 //解决下次选择同样数据不响应,直接将本次数据清空
            $("#fileupload2").val('');
}



3、在上传按钮的点击事件中,再将file_list文件队列安置到formData对象,然后利用ajax向后台发送文件
var formdata=new FormData();
            for(index in file_list){
                if(!isEmptyObject(file_list[index])){
                    file_num=file_num+1;
                    file_total_size=file_total_size+file_list[index].size;
                    formdata.append(file_list_name,file_list[index]);
                }
            }



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值