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的方法,但是现在浏览器的支持率堪忧啊,所以只有曲线救国了。

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

  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯VIP基础练习 矩形面积交
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    核心思想:想清楚自己创业的目的(如果你没有自信提供一种更好的产品或服务,那就别做了,比如IM 电商 搜索)
    在Linux中如何利用backtrace信息解决问题
  • 原文地址:https://www.cnblogs.com/jesse007/p/5715694.html
Copyright © 2011-2022 走看看