14

起因

  • 最近在工作中有个上传大文件的需求,原先咨询过组里的大佬给我推荐了百度的webupload,但后来引入之后发现它是基于jquery封装的。由于本身项目是基于vue开发的所以与jquery相关的开源框架就尽量不考虑了。
  • 后来查阅了资料后自己手动实现了文件切片上传到服务器基本需求已经实现,但由于效率及稳定性问题后来决定还是直传文件到七牛云。一开始我使用了表单上传的方式实现了,后来种种原因又要求我做成分片上传。。
  • 那我只好引入七牛的jssdk,于是走向了无尽的踩坑之路...

开撸

首先,刚接触一门新的技术我们还是先去官方文档走一圈了解下基本用法~
https://developer.qiniu.com/k...
通读一遍之后我再来讲讲官网给我们埋下了多少个坑。。。

首先官网给我们提供了三个至关重要的链接,嗯楼主好人一生平安。
前面两个都是gayhub链接我们可以下载其sdk源码在项目中引入。如果想预先体验一下上传可以点击第三个链接去官方demo玩一玩。
clipboard.png

后端返回给你的获取token的json格式必须是这种格式的。必须是一个json对象内部包裹着uptoken字段,带上其他字段也是可以的但是必须第一层要能找到uptoken

    {
    "uptoken": "0MLvWPnyya1WtPnXFy9KLyGHyFPNdZceomL...",
    "xxx": "..."
    }

因为在它的sdk源码中是这么获取token的。他会先找定义的option字段中是否有uptoken,如果没有再去找uptoken_url有就发送ajax请求去获取uptoken字段,倘若后端必须要以他的格式为主那你可以修改sdk源码来实现。如果uptoken_url也没有值就回去调用uptoken_func函数都没有则报错,所以这三个必须指定一个.

        // getUptoken maybe called at Init Event or BeforeUpload Event
        // case Init Event, the file param of getUptken will be set a null value
        // if op.uptoken has value, set uptoken with op.uptoken
        // else if op.uptoken_url has value, set uptoken from op.uptoken_url
        // else if op.uptoken_func has value, set uptoken by result of op.uptoken_func
        var getUpToken = function(file) {
            if (op.uptoken) {
                that.token = op.uptoken;
                return;
            } else if (op.uptoken_url) {
                logger.debug("get uptoken from: ", that.uptoken_url);
                // TODO: use mOxie
                var ajax = that.createAjax();
                ajax.open('GET', that.uptoken_url, false);
                ajax.setRequestHeader("If-Modified-Since", "0");
                // ajax.onreadystatechange = function() {
                //     if (ajax.readyState === 4 && ajax.status === 200) {
                //         var res = that.parseJSON(ajax.responseText);
                //         that.token = res.uptoken;
                //     }
                // };
                ajax.send();
                if (ajax.status === 200) {
                    var res = that.parseJSON(ajax.responseText);
                    that.token = res.uptoken;
                    logger.debug("get new uptoken: ", res.uptoken);
                } else {
                    logger.error("get uptoken error: ", ajax.responseText);
                }
                return;
            } else if (op.uptoken_func) {
                logger.debug("get uptoken from uptoken_func");
                that.token = op.uptoken_func(file);
                logger.debug("get new uptoken: ", that.token);
                return;
            } else {
                logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!");
            }
        };

再往下看是这么一段话
clipboard.png
我说你就短短1419行代码还能分片上传断点续传还兼容各大浏览器这不科学啊。原来是基于plupload插件之上封装的。注意plupload已经更新到2.3.1了,并且2.2开始已经把moxie干掉了而七牛云的sdk是需要moxie的,所以如果你引入了2.2及其以上的版本会报这样的错误

Uncaught ReferenceError: mOxie is not defined

此处奉上cdn地址

<script src="https://cdn.staticfile.org/plupload/2.1.9/plupload.full.min.js"></script>

中间的版本号可以更换成你想要的,但是建议大家直接访问cdn地址copy下来用本地路径访问。
七牛sdk还是支持npm引入形式的,你可以通过npm安装

npm install qiniu-js

然后在项目中使用import引入

import 'qiniu-js/dist/qiniu.min.js';

但是这样不方便修改源代码而且修改了源代码之后下一个人接到你的项目重新npm install之后又会有各种坑。所以建议提取出来放到公共路径通过script方式引入,不过我觉得可以自己封装一层通过import引入更好~

经过上面两个步骤我们项目应该是这样的:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vue_test</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->

    <!-- <script src="/static/moxie.js" charset="utf-8"></script>
    <script src="/static/plupload.dev.js" charset="utf-8"></script> -->
    <script src="/static/plupload.full.min.js" charset="utf-8"></script>
    <script src="/static/qiniu.js" charset="utf-8"></script>
  </body>
</html>

此处我是将文件提取到static目录下面,用过vue-cli的应该知道~
这里你可以选择引入plupload生产文件或者替换成上面两种文件就是开发调试版本~

那么现在我们要开撸了~
首先,没有什么是一个div解决不了的,倘若你还想实现拖拽上传那就两个。

<div id="container">
    <div id="pickfiles">上传按钮</div>
</div>

接下来我们要编写配置信息以及处理上传的事件。在vue中我们需要写在mouted函数中。

  var uploader = Qiniu.uploader({
      runtimes: 'html5,flash,html4',      // 上传模式,依次退化(照着官网来就是了)
      browse_button: 'pickfiles',         // 上传选择的点选按钮,必需(记得定义id并且保持一致)
      // 在初始化时,uptoken,uptoken_url,uptoken_func三个参数中必须有一个被设置
      // 切如果提供了多个,其优先级为uptoken > uptoken_url > uptoken_func
      // 其中uptoken是直接提供上传凭证,uptoken_url是提供了获取上传凭证的地址,如果需要定制获取uptoken的过程则可以设置uptoken_func
      uptoken : '<Your upload token>', // uptoken是上传凭证,由其他程序生成
      uptoken_url: '/uptoken',         // Ajax请求uptoken的Url,强烈建议设置(服务端提供)
      uptoken_func: function(){        // 在需要获取uptoken时,该方法会被调用
         // do something(一般是发送手动发送ajax获取到token,如果后端返回格式不跟官方一致又不想该懂源代码可以通过这个方式调整)
         return uptoken;
      },
      get_new_uptoken: false,             // 设置上传文件的时候是否每次都重新获取新的uptoken(没有特殊需求一般为false)
      // downtoken_url: '/downtoken',(未使用到,可以不设置)
      // Ajax请求downToken的Url,私有空间时使用,JS-SDK将向该地址POST文件的key和domain,服务端返回的JSON必须包含url字段,url值为该文件的下载地址
      // unique_names: true,              // 默认false,key为文件名。若开启该选项,JS-SDK会为每个文件自动生成key(文件名)
      // save_key: true,                  // 默认false。若在服务端生成uptoken的上传策略中指定了sava_key,则开启,SDK在前端将不对key进行任何处理
      domain: '<Your bucket domain>',     // bucket域名,下载资源时用到,必需(找后端拿)
      container: 'container',             // 上传区域DOM ID,默认是browser_button的父元素(如果不实现拖拽上传可以不设置)
      max_file_size: '100mb',             // 最大文件体积限制(可以调大)
      flash_swf_url: 'path/of/plupload/Moxie.swf',  //引入flash,相对路径(如果没用到flash上传的话可以不设置,一般支持html5上传的浏览器都不会用到它)
      max_retries: 3,                     // 上传失败最大重试次数(自动帮你续传分片)
      dragdrop: true,                     // 开启可拖曳上传(如果不实现拖拽上传可以不设置)
      drop_element: 'container',          // 拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传(如果不实现拖拽上传可以不设置)
      chunk_size: '4mb',                  // 分块上传时,每块的体积
      auto_start: true,                   // 选择文件后自动上传,若关闭需要自己绑定事件触发上传
      //x_vars : {                        // (未使用到,可以不设置)
      //    查看自定义变量
      //    'time' : function(up,file) {
      //        var time = (new Date()).getTime();
                // do something with 'time'
      //        return time;
      //    },
      //    'size' : function(up,file) {
      //        var size = file.size;
                // do something with 'size'
      //        return size;
      //    }
      //},
      init: {
          'FilesAdded': function(up, files) {
              plupload.each(files, function(file) {
                  // 文件添加进队列后,处理相关的事情
              });
          },
          'BeforeUpload': function(up, file) {
                 // 每个文件上传前,处理相关的事情
                 // (上传文件前做一些处理)
          },
          'UploadProgress': function(up, file) {
                 // 每个文件上传时,处理相关的事情
                 // (可以设置进度条信息)
          },
          'FileUploaded': function(up, file, info) {
                 // 每个文件上传成功后,处理相关的事情
                 // 其中info是文件上传成功后,服务端返回的json,形式如:
                 // {
                 //    "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
                 //    "key": "gogopher.jpg"
                 //  }
                 // 查看简单反馈
                 // var domain = up.getOption('domain');
                 // var res = parseJSON(info);
                 // var sourceLink = domain +"/"+ res.key; 获取上传成功后的文件的Url
          },
          'Error': function(up, err, errTip) {
                 //上传出错时,处理相关的事情
          },
          'UploadComplete': function() {
                 //队列文件处理完毕后,处理相关的事情
          },
          'Key': function(up, file) {
              // 若想在前端对每个文件的key进行个性化处理,可以配置该函数
              // 该配置必须要在unique_names: false,save_key: false时才生效

              var key = "";
              // do something with key here
              // (可以自定义key不设定默认是文件名)
              return key
          }
      }
  });

  // domain为七牛空间对应的域名,选择某个空间后,可通过 空间设置->基本设置->域名设置 查看获取

  // uploader为一个plupload对象,继承了所有plupload的方法

如果页面中由多个上传实例,那无非就是多创建几个upload对象传入指定参数设置~

总结

由于本次项目中只涉及到大文件上传,没有图像处理等相关的api使用经验官方的案例就不多讲了。总结起来七牛云上传的套路就是后台为你提供uptoken或者获取uptoken的接口地址之后上传的时候要带上这个token。返回的字段最好是按照官方的格式来,如果不是的话也可以修改源代码或者在uptoken_func中手动获取,另外如果要修改上传的服务器也是要在qiniu.js中修改

    /**
     * qiniu upload urls
     * 'qiniuUploadUrls' is used to change target when current url is not avaliable
     * @type {Array}
     */
    var qiniuUploadUrls = [
        // "http://upload.qiniu.com",
        // "http://up.qiniu.com",
        "修改成你需要的地址",
    ];

如果使用表单上传的话可以不引用任何插件直接生撸上去。代码实现如下:

html:
        <form id="testform" method="post" enctype="multipart/form-data">
            <input name="key" id="key" type="hidden" value="">
            <input name="token" type="hidden" id="token" value="">
            <input id="userfile" name="file" type="file" />

            <!-- take photo with phone -->
            <!-- <input id="userfile" name="file" accept="image/*" type="file" /> -->

            <!-- take video with phone -->
            <!-- <input id="userfile" name="file" type="file" accept="video/*"/> -->

            <input name="accept" type="hidden" />
        </form>

js:
    upload() {
        const formdata = new FormData(document.getElementById('testform'));
        $.ajax({
            url: '上传的七牛云服务器,后端提供', // 'http://up.qiniu.com'
            method: 'post',
            success: function(data) {
                console.log(data);
            },
        })
        ...
    }

需要注意的是,每个input都需要定义好那么属性,并且token不能为空,需要提前通过ajax去后端获取或者使用后端给定的token否则上传会失败~
后续有新的进展继续更新~

看来还是很多人不太懂怎么用,需要个在线demo来玩玩-。-
我撸了个demo,有需要自取~
https://gitee.com/christboy.n...


Kyrielin
1.1k 声望330 粉丝

Coding is amazing!