• 上传文件小工具


    在前端开发工作中,上传文件是一个很常见的功能,尤其是后台管理方面的项目,经常需要用到。而目前单页面应用(SPA)框架的流行,使得以往上传文件的方式略显笨拙,给开发带来不便。那么今天我们就一起来实现一个非常简洁易用的上传工具。

    如同你看到的标题一样,为什么说是纯js呢?因为我们的目标是:

    1. 不引入任何三方工具(jquery也不用)
    2. 不需要写任何标签和css(反感写样式的童鞋,有没有很期待?)
    3. 支持任何主流前端框架引入使用(这也是纯js实现的好处)
     
    老夫就是任性

    明确了目标,接下来我们需要知道为什么要做这样一个工具,能解决现实工作中的什么问题。

    先回顾一下传统上传文件的方式:

    1. 首先需要写一个表单标签 form,指定请求方式为 post,指定 enctypemultipart/form-data,定义好处理上传文件的 action;
    <form method="POST" action="https://your.domain.com/upload"></form>
    1. 在写好的form标签里添加input标签,typefile,用来选择要上传的文件。
    <input type="file" value="请选择文件" />
    1. 继续添加一个按钮标签,类型为submit,用来提交表单。
    <button type="submit">提交</button>

    完整代码如下

    最终界面可能长这个样子


     
    还能再丑一点么

    由于form表单的提交会导致页面重定向,当我们点击选择文件,然后点击提交按钮后,当前页面重定向到了action所指定的页面,再加上目前很多处理文件上传的action都是基于微服务架构的RESTful接口,你会发现当前页面被刷新变成了接口返回的数据!

    现在我们给form的 target 属性设置 _blank,经过测试发现,当前页面虽然没有刷新,但是action指向的页面在一个新窗口中打开了,而实际需求往往是我们需要在当前页面接收返回的数据,明确告诉用户上传是否成功,很多情况下还需要把上传的文件回显出来(比如图片或者音频资源),因此,我们必须在当前页面处理上传结果。

    我们在刚才的form下面写一个iframe标签,指定id和name,然后将form的target属性设置为这个iframe的id,这样做的目的是将form表单的提交目标指向这个iframe,当提交表单之后,接口返回值便会显示在iframe里面,代码如下。

     
    ......
    </form>
    <iframe id="uploadtarget" name="uploadtarget"></iframe>

    但这样做依然没有达到我们的预期,然而事情似乎变得容易了,我们把iframe隐藏起来,然后用js读取iframe里面的内容(接口返回值)即可,那我们什么时候去读取呢?好在iframe每次加载成功后都会派发onload事件,而上传文件接口的返回值指向了iframe会触发它的onload事件,因此我们只需要为onload事件添加回调函数即可。

     
    ......
    </form>
    <iframe style="display:none" onload="onFrameLoad()" 
      id="uploadtarget" name="uploadtarget"></iframe>
    <script>
      function onFrameLoad(){
        var frame = document.getElementById("uploadtarget");
        var content = frame.contentWindow.document.body.innerHtml;
        //解析content,分析上传接口返回值(略)
      }
    </script>

    通过上面的传统方式我们已经基本实现了无刷新上传文件,并且能相对容易的获取到上传接口的返回数据了。但我们发现这种实现方式的成本还是比较大的,尤其是在如今前端工程化,模块化,组件化要求必不可少的情况下,这样的实现方式显然不能被接受。

    接下来正式进入本文的正题,不写标签,不引入其他工具,用最纯粹的方式实现上面的步骤。


    首先让我们分析一下刚才的示例,其中类型为file的input标签必不可少,因为它是用户访问本地磁盘文件的入口,但基于我们的目标是不让开发者写任何标签,因此这个input必须由我们的工具动态创建,现在我们就来实现这微不足道但又必不可少的第一步。

     
    //先给我们的目标功能起一个名字 easyUpload
    function easyUpload(){
      var input = document.createElement("input");
      input.type = "file";
    }

    接下来我们需要让这个input去浏览本地磁盘以便让用户去选择想要上传的文件,但是问题来了,由于我们的工具不提供任何界面,那如何让用户去点击input呢?很简单,我们只需要调用这个动态input的click方法即可,但是,浏览器出于安全的考虑,选择本地文件必须由用户行为触发,因此我们定义的easyUpload函数也必须在用户事件(如click回调函数)里去调用,这一点请务必遵守。

    input.click(); //代码执行到这里,本地文件选择框便会打开
    //加油,离目标似乎很近了!

    现在假设用户已经选择了想要上传的文件,那我们该如何去获取这个文件呢?很简单,input标签在每次选择了文件之后都会触发onchange事件,我们在这个回调里获取文件即可。通过访问input的files属性,我们可以获取到用户选择的文件集合,这里我们只实现单文件上传,因此我们获取files下标为0的对象。这个对象包含了文件的一些常见属性,如name(文件名)、size(文件大小)、type(文件类型)等等,利用这些属性,我们可以对小工具进行功能的扩展,比如利用size和type可以控制上传文件的大小限制和文件类型要求。这个不在本文讨论的范围内。

    input.onchange = function(){
      var file = input.files[0];
    }
     

    拿到文件之后,我们就需要把它提交给服务端了,在上面的传统方式中,提交文件我们用到了form标签,但由于我们的工具想要尽可能的利用编程式思路去实现文件上传,如果还是像input一样去动态创建dom的话,会觉得不够优雅,虽然一样能解决我们的问题,而且原理也是完全相同的,但我们总是希望自己的代码足够清晰和简介明了。所以,在这里我们放弃动态创建form的方式,而使用FormData来实现。

    让我们先了解一下FormData的定义。

    FormData对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。其主要用于发送表单数据,但亦可用于发送带键数据(keyed data),而独立于表单使用。如果表单enctype属性设为multipart/form-data ,则会使用表单的submit()方法来发送数据,从而,发送数据具有同样形式。

    相信你已经发现了它的好处,FormData完全就是form标签的对象形式,有了它,我们就可以用编程的方式去操作form了,然后通过XMLHttpRequest来提交表单,最后在它的异步回调里轻松的拿到接口数据!废话少说,秀代码!

    var form = new FormData();
    form.append("file", file); //第一个参数是后台读取的请求key值
    form.append("fileName", file.name);
    form.append("other", "666666"); //实际业务的其他请求参数
    var xhr = new XMLHttpRequest();
    var action = "http://localhost:8080/upload.do"; //上传服务的接口地址
    xhr.open("POST", action);
    xhr.send(form); //发送表单数据
    xhr.onreadystatechange = function(){
      if(xhr.readyState==4 && xhr.status==200){
        var resultObj = JSON.parse(xhr.responseText);
        //处理返回的数据......
      }
    }
     

    通过这段示例代码我们不难发现,使用FormData我们可以很轻松的把文件添加到表单里,并为这个文件指定参数名,文件名,以及其他跟实际上传业务相关的附加参数。更重要的是,利用FormData我们还可以实现在线录音,在线签字,在线截图以及你能想到的更多可能,这方面的内容也不在本文讨论的范围内,如果你感兴趣,欢迎在评论区域留言,我会抽时间来写这方面的文章。

    上面的代码也为我们展示了如何用XMLHttpRequest来提交表单,以及如何获取接口数据,其中有几个关键点需要注意,action变量和传统方式表单的action属性值是一样的,是用来处理上传文件的接口地址,这个变量我们可以作为参数传递给小工具,然后我们需要调用XMLHttpRequest对象的open方法,open方法的第一个参数定义了请求类型,这里我们务必要使用post类型,open方法的第二个参数就是上面定义的action,最后我们调用send方法将表单对象作为参数发送出去,别忘了在onreadystatechange回调函数里接收接口数据,像这样,一个最原始的Ajax请求就完成了,虽然原始,但它是解决问题最简单,最直接有效的方式。固然npm上有丰富的工具,几乎涵盖了我们能遇到的所有问题,但有时候解决问题可能只需要很简单的几步,在这种情况下,如果还是去安装三方依赖的话,反而把简单问题复杂化了,这个话题仁者见仁,不再过多谈论。

    下面是全部示例代码:

    function easyUpload(){
      var input = document.createElement("input");
      input.type = "file";
      input.click();
      input.onchange = function(){
        var file = input.files[0];
        var form = new FormData();
        form.append("file", file); //第一个参数是后台读取的请求key值
        form.append("fileName", file.name);
        form.append("other", "666666"); //实际业务的其他请求参数
        var xhr = new XMLHttpRequest();
        var action = "http://localhost:8080/upload.do"; //上传服务的接口地址
        xhr.open("POST", action);
        xhr.send(form); //发送表单数据
        xhr.onreadystatechange = function(){
          if(xhr.readyState==4 && xhr.status==200){
            var resultObj = JSON.parse(xhr.responseText);
            //处理返回的数据......
          }
        }
      }
    }



    作者:_乾_
    链接:https://www.jianshu.com/p/5147e36cf19c

  • 相关阅读:
    javascript--运算符
    线程池 的创建小列子,以及参数的介绍
    @SpringBootApplication注解
    SpringBoot 基础知识学习(二)——配置文件多环境配置
    springboot 配置文件读取的两种方式,以及使用到的注解解释
    用虚拟机安装了一台Linux系统,突然想克隆一台服务器,克隆后发现无法上网,如何解决?
    ---oracle 数据库的设计,PL/SQL(loop,for,if,case,while)
    xml的解析技术, 它们之间的区别?
    -----oracle优化之表分区
    --------oracle 的伪表和伪劣,简单的分页
  • 原文地址:https://www.cnblogs.com/xxzb/p/12675040.html
Copyright © 2020-2023  润新知