zoukankan      html  css  js  c++  java
  • 上传图片,多图上传,预览功能,js原生无依赖

    最近很好奇前端的文件上传功能,因为公司要求做一个支持图片预览的图片上传插件,所以自己搜了很多相关的插件,虽然功能很多,但有些地方不能根据公司的想法去修改,而且需要依赖jQuery或Bootstrap库,所以我就想学下图片上传的原理,试着做一个原生无依赖,而且足够灵活的图片上传插件。话不多说,开整。

    1. 大体思路

    1.1 首先我们需要考虑用户如何使用我们的插件。

    用户引入插件代码后,只需要像下面这样,设置一些参数,然后执行一个方法就生成一个图片上传组件。

    <div id="upload"></div>  // 这是用来生成图片上传组件的div
    <script>
    // 设置参数
    var options = {
        path: '/',    // 上传图片时指定的地址路径,类似form变淡的action属性
        onSuccess: function (res) {    // 上传成功后执行的方法,res是接收的ajax响应内容
            console.log(res);  
        },
        onFailure: function (res) {    // 上传失败后执行的方法,res是接收的ajax响应内容
            console.log(res);
        }
    }
    // 执行生成图片上传插件的方法, 第一个参数是上面提到的准备生成组件的div选择器,第二个参数是设置的组件信息,执行方法后返回一个函数指针,指向执行上传功能的函数,通过把执行上传功能的函数暴露出来,用户就可以自己控制何时上传图片了。
    var upload = tinyImgUpload('#upload', options);  
    </script>

    1.2 代码设计

    我们需要思考用户如何引入我们的插件代码。
    插件代码应该分为两个文件,一个CSS文件(tinyImgUpload.css),用于定义组件的基本样式,此外用户可以根据自己的想法自己DIY样式,另一个是控制功能逻辑的js文件(tinyImgUpload.js)。用户引入这两个文件后,就可以实现图片上传组件了。

    2. 具体实现

    具体实现的时候,主要涉及到两个地方,一个是读取本地文件,实现图片上传前可以预览的功能,一个是图片上传功能。

    2.1 读取本地文件实现预览

    这里用到了html5的File API,使用这个API可以在客户端验证上传的文件类型,限制文件大小,当然,在这里我们主要用到FileReader接口来读取文件,Filereader.readAsDataURL()返回的事件对象的result属性就是将文件编码为base64的数据地址,类似下面这样的,把他赋值给src属性,图片就显示出来了。

    具体代码如下,完整代码可以从这里下载(tinyImgUpload.js )

    // 预览图片
    //处理input选择的图片
    function handleFileSelect(evt) {
        var files = evt.target.files;
    
        for(var i=0, f; f=files[i];i++){
            // 过滤掉非图片类型文件
            if(!f.type.match('image.*')){
                continue;
            }
            // 过滤掉重复上传的图片
            var tip = false;
            for(var j=0; j<(ele.files).length; j++){
                if((ele.files)[j].name == f.name){
                    tip = true;
                    break;
                }
            }
            if(!tip){
                // 图片文件绑定到容器元素上
                ele.files.push(f);
    
                var reader = new FileReader();
                reader.onload = (function (theFile) {
                    return function (e) {
                        var oDiv = document.createElement('div');
                        oDiv.className = 'img-thumb img-item';
                        // 向图片容器里添加元素
                        oDiv.innerHTML = '<img class="thumb-icon" src="'+e.target.result+'" />'+
                                        '<a href="javscript:;" class="img-remove">x</a>'
    
                        ele.insertBefore(oDiv, addBtn);
                    };
                })(f);
    
                reader.readAsDataURL(f);
            }
        }
    }
    // input#img-file-input是一个隐藏的上传图片的input控件,当选择图片的时候会触发change事件
    document.querySelector('#img-file-input').addEventListener('change', handleFileSelect, false);

    2.2 上传图片

    2.2.1 准备文件对象

    上传文件之前,我们需要考虑如何保存用户已经选择的文件对象,由于用户可能多次选择,也可能在上传之前又删除了几个图片,所以需要有一个地方实时保存图片信息,并且要和预览的图片保持同步,预览显示有哪几张图片,这个地方就存储几张图片。我采用的方式是将文件信息组装成一个数组,然后绑定到组件元素(#img-container)的自定义属性上,上面代码中的“ele.files.push(f)”做的就是这件事。

    2.2.2 文件对象我们准备好后,下一步就是上传了

    ajax是不能直接上传文件对象的,我们可以通过FormData对象,FormData是XMLHttpRequest Level 2添加的一个新接口,使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest异步发送这个"表单"。具体代码如下。

    // 上传图片
    function uploadImg() {
        var xhr = new XMLHttpRequest();
        var formData = new FormData();
    
        for(var i=0, f; f=ele.files[i]; i++){
            formData.append('files', f);
        }
    
        xhr.onreadystatechange = function (e) {
            if(xhr.readyState == 4){
                if(xhr.status == 200){
                    options.onSuccess(xhr.responseText);
                }else {
                    options.onFailure(xhr.responseText);
                }
            }
        }
    
        xhr.open('POST', options.path, true);
        xhr.send(formData);
    }

    2.3 设置样式

    我们写了一个基本的布局样式作为默认样式,用户可以根据自己的需求进行DIY。这里是完整的样式文件(tinyImgUpload.css )。
    基本的效果图如下。

    如果我们想触发上传图片,可以把tinyImgUpload('#upload', options)返回的upload方法绑定到一个按钮上,监听点击事件。

    <button class="submit">submit</button>
    <script>
    document.getElementsByClassName('submit')[0].onclick = function (e) {
        upload();
    }
    </script>

    这样当我点击图片的时候,图片就会上传,交给服务器端处理了。

    为了测试图片上传好不好用,我自己搭建了一个图片接收的服务器,使用的是node.js,通过multer实现,如果大家感兴趣可以点击这里

    3 总结

    图片上传的关键部分就是如何读取本地文件实现预览,以及通过FormData对象构造一个表单对象实现ajax异步上传文件。目前这个插件的功能还不够完善,我把它放到了Github上(https://github.com/lguow/tinyImgUpload),后续会慢慢优化,欢迎大家提出宝贵意见。

    js 实现异步上传图片+预览

    两种js实现方式,一种用原生的ajax;另一种用JQuery,例子比较简单,直接上代码。

    
    
    <!DOCTYPE html>
    <html>
    <head>
        <title>Title</title>
        <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    </head>
    <body style="overflow: scroll; overflow-y: hidden; overflow-x: hidden">
        <div style="height: 20px"></div>
        <div class="container">
            <div class="row">
    
                <div class="col-md-6 col-md-offset-3">
                    <form class="form-horizontal" enctype="multipart/form-data" role="form" id="testform">
    
                        <input type="hidden" value="1" id="id" name="id" />
                        <div class="control-group">
                            <label for="uname" class="col-md-3 control-label span3">姓 名:</label>
                            <div class="col-md-9">
                                <input type="text" class="form-control span3" value="" id="uname" name="uname"
                                    placeholder="请输入姓名">
                            </div>
                        </div>
    
                        <div class="control-group">
                            <label for="pwd" class="col-md-3 control-label span3">密码:</label>
                            <div class="col-md-9">
                                <input type="password" class="form-control span3" value="" id="pwd" name="pwd"
                                    placeholder="请输入密码">
                            </div>
                        </div>
    
                        <div class="control-group">
                            <label class="col-md-3 control-label span3"></label>
                            <div class="col-md-9">
                                <img src="" width="100px" height="100px">
                            </div>
                        </div>
    
                        <div class="control-group">
    
                            <label for="requirement" class="col-md-3 control-label span3">图片上传</label>
                            <div class="col-md-9">
                                <div class="input-group">
                                    <input id="photoCover" class="form-control" readonly type="text">
                                    <label class="input-group-btn">
                                        <input id="file" type="file" name="file" style="left: -9999px; position: absolute;">
                                        <span class="btn btn-default">Browse</span>
                                    </label>
                                </div>
                            </div>
                        </div>
    
                        <div class="control-group">
                            <label class="col-md-2 control-label span2"></label>
                            <div class="col-md-10">
                                <a class="btn btn-small btn-primary" onclick="saveUser()">方式一</a>
                                <a class="btn btn-small btn-danger" onclick="saveUser2()">方式二</a>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
        <script>
    
            //html5实现图片预览功能
            $(function () {
                $("#file").change(function (e) {
                    var file = e.target.files[0] || e.dataTransfer.files[0];
                    $('#photoCover').val(document.getElementById("file").files[0].name);
                    if (file) {
                        var reader = new FileReader();
                        reader.onload = function () {
                            $("img").attr("src", this.result);
                        }
    
                        reader.readAsDataURL(file);
                    }
                });
            })
            //方式一 Jquery实现
            function saveUser2() {
                var id = $("#id").val().trim();
                var uname = $("#uname").val().trim();
                var pwd = $("#pwd").val().trim();
                var file = document.getElementById("file").files[0];
                var formData = new FormData();
                formData.append('id', id);
                formData.append('uname', uname);
                formData.append('pwd', pwd);
                formData.append('file', file);
    
                $.ajax({
                    url: "/home/about",
                    type: "post",
                    data: formData,
                    contentType: false,
                    processData: false,
                    mimeType: "multipart/form-data",
                    success: function (data) {
                        console.log(data);
                    },
                    error: function (data) {
                        console.log(data);
                    }
                });
            }
    
            //方式二  原生ajax实现
            function saveUser() {
                var id = $("#id").val().trim();
                var uname = $("#uname").val().trim();
                var pwd = $("#pwd").val().trim();
                var file = document.getElementById("file").files[0];
    
                //原生ajax实现文件上传
                var form = new FormData();
                form.append("uname", uname); // 可以增加表单数据
                form.append("id", id);
                form.append("pwd", pwd);
                if (file) {
                    form.append("file", file);
                }
    
                //得到xhr对象
                var xhr = null;
                if (XMLHttpRequest) {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
    
                xhr.open("post", "/home/about", true);//设置提交方式,url,异步提交
                xhr.onload = function () {
                    var data = xhr.responseText;    //得到返回值
                    alert(data);
                }
                xhr.send(form);
            }
        </script>
    </body>
    </html>
  • 相关阅读:
    机器学习分类
    12.python中高阶函数以及内建函数(map,filter,sorted),和闭包
    python中的异常处理相关语句和迭代器
    和好朋友渐行渐远是什么感觉?
    MDX学习笔记(整理) MDX语法
    Cognos软件介绍文档(原创)
    隐藏
    android::Mutex::Autolock的使用
    Android中sp和wp的使用
    Day 07 字符编码,文件操作
  • 原文地址:https://www.cnblogs.com/lguow/p/9375771.html
Copyright © 2011-2022 走看看