- 快速使用
- 基础参数选项
- 上传接口
- 回调

- 选择文件的回调
- 上传前的回调
- 请求成功的回调
- 请求失败的回调
- 多文件上传回调
- 上传进度的回调

- 重载实例
- 重新上传
- 跨域上传

-

#  图片/文件上传 - layui.upload

上传模块自 layui 2.0 的版本开始，进行了全面重写，这使得它不再那么单一，它所包含的不仅是更为强劲的功能，还有灵活的 UI。任何元素都可以作为上传组件来调用，譬如按钮、图片、普通的 DIV 等等，而不再是一个单调的 file 文件域。

模块加载名称：upload

快速使用

一切从小试牛刀开始。通常情况下，我们上传文件是借助 type="file" 的 input 标签来完成的，但非常遗憾的是，它不能很好地与其它表单元素并存，所以我们常常要单独为它做一个业务层面的“异步上传”，即先让图片上传，再和其它表单一起提交保存。下面是一个小示例：

这原本只是一个普通的 button，正是 upload 模块赋予了它“文件选择”的特殊技能。当然，你还可以随意定制它的样式，而不是只局限于按钮。

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>upload模块快速使用</title>
      <link rel="stylesheet" href="/static/build/layui.css" media="all">
    </head>
    <body>

    <button type="button" class="layui-btn" id="test1">
      <i class="layui-icon">&#xe67c;</i>上传图片
    </button>

    <script src="/static/build/layui.js"></script>
    <script>
    layui.use('upload', function(){
      var upload = layui.upload;

      //执行实例
      var uploadInst = upload.render({
        elem: '#test1' //绑定元素
        ,url: '/upload/' //上传接口
        ,done: function(res){
          //上传完毕回调
        }
        ,error: function(){
          //请求异常回调
        }
      });
    });
    </script>
    </body>
    </html>

一切看起来是那样的简单，乃至于我不得不凑一段文字来填充这一行的版面。这样好像与下文衔接起来会比较谐调的样子（自我感觉）

核心方法与基础参数选项

使用 upload 模块必须与 upload.render(options) 方法打交道，其中的 options即为基础参数，它是一个对象。

    var upload = layui.upload; //得到 upload 对象

    //创建一个上传组件
    upload.render({
      elem: '#id'
      ,url: ''
      ,done: function(res, index, upload){ //上传后的回调

      }
      //,accept: 'file' //允许上传的文件类型
      //,size: 50 //最大允许上传的文件大小
      //,……
    })

从 layui 2.1.0 开始，允许你直接在元素上设定基础参数，如：

    【HTML】
    <button class="layui-btn test" lay-data="{url: '/a/'}">上传图片</button>
    <button class="layui-btn test" lay-data="{url: '/b/', accept: 'file'}">上传文件</button>

    【JS】
    upload.render({
      elem: '.test'
      ,done: function(res, index, upload){
        //获取当前触发上传的元素，一般用于 elem 绑定 class 的情况，注意：此乃 layui 2.1.0 新增
        var item = this.item;
      }
    })

更多支持的参数详见下表，合理的配置它们，应对各式各样的业务需求。

参数选项 | 说明 | 类型 | 默认值
--- | --- | --- | ---
elem | 指向容器选择器，如：elem: '#id'。也可以是DOM对象 | string/object | -
url | 服务端上传接口，返回的数据规范请详见下文 | string | -

data

请求上传接口的额外参数。如：data: {id: 'xxx'}
从 layui 2.2.6 开始，支持动态值，如:

    data: {
      id: function(){
        return $('#id').val();
      }
    }

object

-

headers | 接口的请求头。如：headers: {token: 'sasasas'}。注：该参数为 layui 2.2.6 开始新增
accept | 指定允许上传时校验的文件类型，可选值有：images（图片）、file（所有文件）、video（视频）、audio（音频） | string | images
acceptMime | 规定打开文件选择框时，筛选出的文件类型，值为用逗号隔开的 MIME 类型列表。如： acceptMime: 'image/*'（只显示图片文件） acceptMime: 'image/jpg, image/png'（只显示 jpg 和 png 文件） 注：该参数为 layui 2.2.6 开始新增 | string | images
exts | 允许上传的文件后缀。一般结合 accept 参数类设定。假设 accept 为 file 类型时，那么你设置 exts: 'zip|rar|7z' 即代表只允许上传压缩格式的文件。如果 accept 未设定，那么限制的就是图片的文件格式 | string | jpg|png|gif|bmp|jpeg
auto | 是否选完文件后自动上传。如果设定 false，那么需要设置 bindAction 参数来指向一个其它按钮提交上传 | boolean | true
bindAction | 指向一个按钮触发上传，一般配合 auto: false 来使用。值为选择器或DOM对象，如：bindAction: '#btn' | string/object | -
field | 设定文件域的字段名 | string | file
size | 设置文件最大可允许上传的大小，单位 KB。不支持ie8/9 | number | 0（即不限制）
multiple | 是否允许多文件上传。设置 true即可开启。不支持ie8/9 | boolean | false
number | 设置同时可上传的文件数量，一般配合 multiple 参数出现。 注意：该参数为 layui 2.2.3 开始新增 | number | 0（即不限制）
drag | 是否接受拖拽的文件上传，设置 false 可禁用。不支持ie8/9 | boolean | true
回调
choose | 选择文件后的回调函数。返回一个object参数，详见下文 | function | -
before | 文件提交上传前的回调。返回一个object参数（同上），详见下文 | function | -
done | 执行上传请求后的回调。返回三个参数，分别为：res（服务端响应信息）、index（当前文件的索引）、upload（重新上传的方法，一般在文件上传失败后使用）。详见下文 | function | -
error | 执行上传请求出现异常的回调（一般为网络异常、URL 404等）。返回两个参数，分别为：index（当前文件的索引）、upload（重新上传的方法）。详见下文 | function | -

上传接口

设定一个 URL 地址给 url 参数，用来告诉 upload 模块的服务端上传接口。像你平时使用Ajax一样。如：

    upload.render({
      elem: '#id'
      ,url: '/api/upload/' //必填项
      ,method: ''  //可选项。HTTP类型，默认post
      ,data: {} //可选项。额外的参数，如：{id: 123, abc: 'xxx'}
    });

该接口返回的相应信息（response）必须是一个标准的 JSON 格式，如：

    {
      "code": 0
      ,"msg": ""
      ,"data": {
        "src": "http://cdn.layui.com/123.jpg"
      }
    }

注意1：你不一定非得按照上述格式返回，只要是合法的 JSON 字符即可。其响应信息会转化成JS对象传递给 done 回调。
注意2：如果上传后，出现文件下载框（一般为ie下），那么你需要在服务端对response的header设置 Content-Type: text/html

选择文件的回调

在文件被选择后触发，该回调会在 before 回调之前。一般用于非自动上传（即 auto: false ）的场景，比如预览图片等。

    upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,auto: false //选择文件后不自动上传
      ,bindAction: '#testListAction' //指向一个按钮触发上传
      ,choose: function(obj){
        //将每次选择的文件追加到文件队列
        var files = obj.pushFile();

        //预读本地文件，如果是多文件，则会遍历。(不支持ie8/9)
        obj.preview(function(index, file, result){
          console.log(index); //得到文件索引
          console.log(file); //得到文件对象
          console.log(result); //得到文件base64编码，比如图片

          //obj.resetFile(index, file, '123.jpg'); //重命名文件名，layui 2.3.0 开始新增

          //这里还可以做一些 append 文件列表 DOM 的操作

          //obj.upload(index, file); //对上传失败的单个文件重新上传，一般在某个事件中使用
          //delete files[index]; //删除列表中对应的文件，一般在某个事件中使用
        });
      }
    });

事实上这是一个非常实用的存在，可轻松应对复杂的列表文件上传管理。具体可移步到 示例 页面，里面有一个文件列表的小例子。

文件上传前的回调

在 choose 回调之后、done/error 回调之前触发。返回的参数完全类似 choose 回调。一般用于上传完毕前的loading、图片预览等。

    upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,before: function(obj){ //obj参数包含的信息，跟 choose回调完全一致，可参见上文。
        layer.load(); //上传loading
      }
      ,done: function(res, index, upload){
        layer.closeAll('loading'); //关闭loading
      }
      ,error: function(index, upload){
        layer.closeAll('loading'); //关闭loading
      }
    });

上传接口请求成功的回调

在上传接口请求完毕后触发，但文件不一定是上传成功的，只是接口的响应状态正常（200）。回调返回三个参数，分别为：服务端响应信息、当前文件的索引、重新上传的方法

    upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,done: function(res, index, upload){
        //假设code=0代表上传成功
        if(res.code == 0){
          //do something （比如将res返回的图片链接保存到表单的隐藏域）
        }

        //获取当前触发上传的元素，一般用于 elem 绑定 class 的情况，注意：此乃 layui 2.1.0 新增
        var item = this.item;

        //文件保存失败
        //do something
      }
    });

上传请求失败的回调

当请求上传时出现异常时触发（如网络异常、404/500等）。回调返回两个参数，分别为：当前文件的索引、重新上传的方法

    upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,error: function(index, upload){
        //当上传失败时，你可以生成一个“重新上传”的按钮，点击该按钮时，执行 upload() 方法即可实现重新上传
      }
    });

多文件上传完毕后的状态回调

只有当开启多文件时（即 multiple: true），该回调才会被触发。回调返回一个 object 类型的参数，包含一些状态数据：

    upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,multiple: true
      ,allDone: function(obj){ //当文件全部被提交后，才触发
        console.log(obj.total); //得到总文件数
        console.log(obj.successful); //请求成功的文件数
        console.log(obj.aborted); //请求失败的文件数
      }
      ,done: function(res, index, upload){ //每个文件提交一次触发一次。详见“请求成功的回调”

      }
    });

文件上传进度的回调

在网速一般的情况下，大文件的上传通常需要一定时间的等待，而浏览器并不会醒目地告知你它正在努力地上传中，此时为了提升用户体验，我们可以通过该回调制作一个进度条。注：该回调为 layui 2.5.5 新增

    upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,progress: function(n, elem, res, index){
        var percent = n + '%' //获取进度百分比
        element.progress('demo', percent); //可配合 layui 进度条元素使用

        console.log(elem); //得到当前触发的元素 DOM 对象。可通过该元素定义的属性值匹配到对应的进度条。
        console.log(res); //得到 progress 响应信息
        console.log(index); //得到当前上传文件的索引，多文件上传时的进度条控制，如：
        element.progress('demo-'+ index, n + '%'); //进度条
      }
    });

重载实例

有时你可能需要对 upload.render() 实例进行重载，通过改变一些参数（如将上传文件重置为只上传图片等场景）来重置功能。如：

    //创建一个实例
    var uploadInst = upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,size: 1024*5 //限定大小
    });

    //重载该实例，支持重载全部基础参数
    uploadInst.reload({
      accept: 'images' //只允许上传图片
      ,acceptMime: 'image/*' //只筛选图片
      ,size: 1024*2 //限定大小
    });

注意：该方法为 layui 2.5.0 开始新增

重新上传

在执行 upload.render(options) 方法时，其实有返回一个实例对象，以便对完成重新上传等操作。注意：这是对当前上传队列的全局重新上传，而 choose 回调返回的 obj.upload(index, file) 方法则是对单个文件进行重新上传。如：

    var uploadInst = upload.render({
      elem: '#id'
      ,url: '/api/upload/'
      ,choose: function(obj){
        obj.preview(function(index, file, result){
          //对上传失败的单个文件重新上传，一般在某个事件中使用
          //obj.upload(index, file);
        });
      }
    });

    //重新上传的方法，一般在某个事件中使用
    //uploadInst.upload();

跨域上传

有些时候，可能会涉及到文件跨域操作，过去版本的 upload 模块最大的缺陷恰恰在于这里。而从 layui 2.0 的版本开始，我们已经对 跨域做了支持。但鉴于代码的冗余度等多方面考虑，在IE9以下版本环境中，仍然不支持跨域。其它所有版本的IE和Chrome/FireFox等高级浏览器均支持。

那么，需要你怎么做？通常来说，是借助 CORS（跨资源共享） 方案，即对接口所在的服务器设置：Access-Control-Allow-Origin 详见Google，配置起来还是挺简单的。而至于域名限制，一般是服务端程序中去做处理。这里不做过多赘述。