zoukankan      html  css  js  c++  java
  • web前端之文件上传

    前言

    最近太忙一直没时间认真的写博客(哈哈哈),最近pm提一个需求,移动端需要一个上传图片的功能,允许多选、删除、预览、点击查看大图并可以滑动。虽然听起来很多,但是这个功能在web上实现过啊,使用webuploader妥妥的,然后就拍着胸口答应了下来,并让B同事做。

    开发完成后,后端说同一次上传多张图只能发一次请求,纳尼。。我没仔细看过webuploader的API,不知道为什么需要单独进行上传,看了一下请求信息,在支持FormData的浏览器中使用的FormData来模拟form。那既然这样也可以支持多图一起请求啊(如果webuploader支持或者有其他考虑,欢迎大家指出),算了,自己造吧,也就有了这篇随笔。

    1.form表单提交

    像我这样的小白都知道form表单提交之后要刷新页面,更别说要实现pm提的那些需求了,但是代码还是要上一下的。

    <form action="http://baidu.com" target="" id="uploadForm" enctype="multipart/form-data">
      <input id="file" type="file" name="file"/>
      <input type="submit" name="submit" id="submit" value="upload" />
    </form>

    因为我们这里要上传文件,所以enctype的值为multipart/form-data。

    2.form+iframe

    看到上面的代码,form有一个target的属性,规定在何处打开action,可能的值有

    • _blank
    • _self
    • _parent
    • _top
    • framename

    就不一一介绍了,我们最关心的是最后一个值,framename,我们将页面放在iframe里处理就不担心刷新的问题了,然后再设置一个回调就可以处理服务端返回的参数

    html:

    <form action="http://baidu.com"  target="" id="uploadForm" enctype="multipart/form-data">
           <input id="file" type="file" name="file"/>
           <input type="submit" name="submit" id="submit" value="upload" />
    </form>

    jq:

    <script type="text/javascript">
            var form = $("#uploadForm");
                form.on("submit",function(){
                    var seed = Math.floor(Math.random()*1000),
                    id = "uploader-iframe" + seed,
                    callback = "uploader-cb" + seed,
                    iframe = $("<iframe id='"+id+"' name='"+id+"' style='display:none'></iframe>"),
                    url = form.attr("action");
                    form.attr("target",id).append(iframe).attr("action",url+"?iframe="+callback);
                    window[callback]=function(data){
                        iframe.remove();
                        form.removeAttr("target");
                        form.attr("action",url);
                        window[callback] = undefined;
                    }
                })
            </script>

    有没有觉得和jsonp的方式有点像,但是这里不需要动态创建script标签来调用,因为iframe本来就是一个页面,只需要服务端返回调用方法和数据在iframe页面就ok了

    服务端返回:

    <script type="text/javascript">
    window.top.window[callback](data)
    </script>

    callback是我们事前约定好并传给服务器的参数,data为服务器返回的数据。

    还有一种拿数据的方法,不通过后端回调

    iframe.on("load",function(){
           var ifr =$(this).contents() //jq对象document
            //ifr = this.contentDocument || this.document//兼容ie
    })

    跟后端约定返回数据格式,然后进行操作

    form+iframe这种伪异步的提交方式对文件的处理还是无力,不能想删就删,预览图片只有先传给后台,后台再返回一个线上的地址

    3.HTML5之FormData、FileReader

    当当当。。文章的主角出现

    利用FormData模拟表单数据,通过ajax进行提交,FileReader的readAsDataURL方法拿到base64地址来预览(完美,注意兼容性)

    form表单初始化FormData提交

    <form action="http://baidu.com"  target="" id="uploadForm" enctype="multipart/form-data">
                <input id="file" type="file" name="file"/>
                <input type="submit" name="submit" id="submit" value="upload" />
            </form>
    $.ajax({
        url: '/upload',
        type: 'POST',
        cache: false,
        data: new FormData($('#uploadForm')[0]),
        processData: false,
        contentType: false
    }).done(function(res) {
    }).fail(function(res) {});
    • processData设置为false。因为data值是FormData对象,不需要对数据做处理。
    • <form>标签添加enctype="multipart/form-data"属性。
    • cache设置为false,上传文件不需要缓存。
    • contentType设置为false。因为是由<form>表单构造的FormData对象,且已经声明了属性enctype="multipart/form-data",所以这里设置为false。

    上传后服务端通过file来接收文件流。

     通过FormData对象append方法来添加

    <div id="uploadForm">
        <input id="file" type="file"/>
        <button id="upload" type="button">upload</button>
    </div>
    var formData = new FormData();
    formData.append('file', $('#file')[0].files[0]);
    $.ajax({
        url: '/upload',
        type: 'POST',
        cache: false,
        data: formData,
        processData: false,
        contentType: false
    }).done(function(res) {
    }).fail(function(res) {});

    FileReader获取DataUrl

    <input multiple="multiple" id="file" type="file" name="file"/>
    var reader = new FileReader();
    reader.onload=function(e){
        //e.target.result为$("#file")[0].files[0]的base64地址
    }
    reader.readAsDataURL($("#file")[0].files[0])

    更多FormData和FileReader方法可以去查看一下API

    4.flash实现

    flash的实现不在我们讨论的范围,而且浏览器的支持对flash有很大的影响,现在有很多上传组件做了低版本flash的兼容。比如webuploader、uploadify等

    需求实现

     说了这么多,最后还是要回归到需求上来,由于这个需求是在移动端上,我们自然而然就选了第三种实现方式,这里讲一下实现的思路(伪代码)。

    var formdata = new FormData(), count = 0, mId = '${model.id}', a = [];
        formdata.append("id", mId)
        $("#file").on("change",function() {//file触发change时循环files做相应的处理
                            if ($("#file")[0].files.length > 0) {
                                
                                for (var i = 0, j = $("#file")[0].files.length; i < j; i += 1) {
    
                                    (function(k) {//按顺序插入预览图片,并在数组中保存对应的files
                                        var reader = new FileReader()
                                        reader.onload = function(e) {//保证预览顺序和files数组顺序一致,方便后面删除file
                                            count += 1;
                                           a.push($("#file")[0].files[k]);
                                            $(".filelist").append("<li><p class='imgWrap'><img class='close' src='img/close.png'/><img id='"
                                                                    + $("#file")[0].files[k].name
                                                                    + "' class='choose-img' src='"
                                                                    + e.target.result
                                                                    + "'' /></p></li>");//预览相关处理
    $(".swiper-wrapper").append("<div class='swiper-slide'><img id='"
                                                + $("#file")[0].files[k].name
                                                + "' class='choose-img' src='"
                                                + e.target.result
                                                + "'' /></div>");//点击查看大图相关处理
                                        }
                                        
                                        
                                        reader.readAsDataURL($("#file")[0].files[i]);
                                    })(i);
    
                                }
                                
                            }
                        });
        $(document).on("click", ".close", function() {//图片删除处理,处理files数组,更新count
            var $this = $(this);
            a.splice($this.parents("li").index(), 1);
            $(".swiper-slide").eq($this.parents("li").index()).remove();//如果考虑复用,这个index可以优化下
            count -= 1;
            $this.parents("li").remove();
            $("#jsUpload").show();
        })
        $("#jsUploadBtn").on("click", function() {//上传时,将files数组循环append进FromData进行ajax提交
            for (var i = 0, j = a.length; i < j; i += 1) {
                formdata.append("file", a[i])
            }
            $.ajax({
                url : '',
                type : 'POST',
                cache : false,
                data : formdata,
                processData : false,
                contentType : false
            }).done(function(data) {
                if (data == 'error') {
                    $(".flie-toast").addClass("hide");
                    toast("上传失败,请联系管理员!");
                } else if (data == 'no') {
                    $(".flie-toast").addClass("hide");
                    toast("您没有参与比赛,不允许上传截图");
                } else if (data == 'yes') {
                    $(".flie-toast").addClass("hide");
                    toast("上传成功");
                    setTimeout(function() {
                        window.location.href = "";
                    }, 2000);
                } else {
                    $(".flie-toast").addClass("hide");
                    toast("上传失败,请联系管理员!");
                }
    
            }).fail(function(res) {
            });
    
        })

    虽然FormData对象有一个delete的方法,但是现在浏览器的支持率堪忧啊,所以只有曲线救国了。

    个人知识的宽度和广度毕竟有限,如文章有什么疏漏和错误的地方,欢迎大家留言指出。

  • 相关阅读:
    CEPH篇 CEPH接口(S3)
    MAC篇 超级简单的连接远程主机方法(永久记住)
    【思维论01】如何让自己像打王者荣耀一样发了疯、石乐志的学习?
    CEPH篇 CEPH部署(在线和离线 Centos 7)
    JAVA优化篇 如何从茫茫日志中找到运行缓慢的线程
    CEPH篇 块存储、文件存储和对象存储意义和差异
    CEPH篇 简介及关键对象解释
    混合使用Jquery Deferred和Angularjs的$timeout(转)
    AngularJs应用页面切换优化方案(转)
    在IE7下使用angularjs(转)
  • 原文地址:https://www.cnblogs.com/jesse007/p/5715694.html
Copyright © 2011-2022 走看看