资讯专栏INFORMATION COLUMN

「HTML5」FileAPI 文件操作实战

Jacendfeng / 1554人阅读

摘要:准备工作首先,我们的来自于标签中选中的文件列表。用户选中的文件信息也会传入回调函数的第一个参数中。唯一需要特殊处理的是文件对象的获取入口改变了。对于标签,监听事件,存放在中对于拖拽操作,存放在拖拽事件的回调函数参数里,通过访问即可。

本文来自《FileAPI 文件操作实战》

其他所有系列都放在了Github。欢迎交流和Star

介绍

HTML5 为我们提供了 File API 相关规范。主要涉及 File 接口 和 FileReader 对象 。

本文整理了兼容性检测、文件选择、属性读取、文件读取、进度监控、大文件分片上传以及拖拽上传等开发中常见的前端文件操作。

准备工作

首先,我们的 File 来自于标签中选中的文件列表。所以,准备如下的 HTML 代码:


检测兼容性

File 对象是特殊类型的 Blob。在 script 入口处,应该检测当前浏览器是否支持 File API:

if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
  throw new Error("当前浏览器对FileAPI的支持不完善");
}
监听文件选择

对于 type 为 file 类型的标签,在选择文件的时候,会触发change事件。用户选中的文件信息也会传入回调函数的第一个参数中。

function handleFileSelect(event) {
  const { files } = event.target;
  if (!files.length) {
    console.log("没有选择文件");
    return;
  }

  console.log("选中的文件信息是:", files);
}

document
  .querySelector("#files")
  .addEventListener("change", handleFileSelect, false);
文件属性-File

event.target.files 是一个FileList对象,它是一个由File对象组成的列表。

每个 File 对象,保存着选中的对应文件的属性。常用的用:

name:文件名

type:文件类型

size:文件大小

下面,通过 type 属性,过滤掉非图片类型的文件,只展示图片类型文件的信息:

function handleFileSelect(event) {
  const { files } = event.target;
  if (!files.length) {
    console.log("没有选择文件");
    return;
  }

  const innerHTML = [];
  const reImage = /image.*/;

  for (let file of files) {
    if (!reImage.test(file.type)) {
      continue;
    }

    innerHTML.push(
      `
      
  • ${file.name} (${file.type || "n/a"}) - ${file.size} bytes
  • ` ); } document.querySelector("#list").innerHTML = `
      ${innerHTML.join("")}
    `; }
    读取文件-FileReader

    还是以图片读取为例,读取并且显示所有的图片类型文件。

    文件读取需要使用FileReader对象,它常用 3 个回调方法:

    onload: 文件读取完成

    onloadstart:文件上传开始

    onprogress : 文件上传中触发

    Image类似,在读取文件之前,需要先绑定事件处理。它读取操作有:readAsArrayBuffer、readAsDataURL、readAsBinaryString、readAsText。传入的参数就是File对象。

    那么这几个方法有什么区别呢?不同的读取方式,回调事件onload接受到的event.target.result不相同。比如,readAsDataURL读取的话,result 是一个图片的 url。

    下面就是读取图片文件,然后展示的一个例子:

    function handleFileSelect(event) {
      let { files } = event.target;
      if (!files.length) {
        return;
      }
    
      let vm = document.createDocumentFragment(),
        re = /image.*/,
        loaded = 0, // 完成加载的图片数量
        total = 0; // 总共图片数量
    
      // 统计image文件数量
      for (let file of files) {
        re.test(file.type) && total++;
      }
    
      // onloadstart回调
      const handleLoadStart = (ev, file) =>
        console.log(`>>> Start load ${file.name}`);
      // onload回调
      const handleOnload = (ev, file) => {
        console.log(`<<< End load ${file.name}`);
    
        const img = document.createElement("img");
        img.height = 250;
        img.width = 250;
        img.src = ev.target.result;
        vm.appendChild(img);
    
        // 完成加载后,将其放入dom元素中
        if (++loaded === total) {
          document.querySelector("#images").appendChild(vm);
        }
      };
    
      for (let file of files) {
        if (!re.test(file.type)) {
          continue;
        }
    
        const reader = new FileReader();
        reader.onloadstart = ev => handleLoadStart(ev, file);
        reader.onload = ev => handleOnload(ev, file);
        // 读取文件对象
        reader.readAsDataURL(file);
      }
    }
    
    document
      .querySelector("#files")
      .addEventListener("change", handleFileSelect, false);
    监控读取进度

    在监控读取进度的时候,主要是处理 FileReader 对象上的 onprogress 事件。

    下面的例子,请打开一个较大的文件来查看效果(否则一下就读取完了):

    function handleFileSelect(event) {
      let { files } = event.target;
      if (!files.length) {
        return;
      }
    
      const handleLoadStart = (ev, file) =>
        console.log(`>>> Start load ${file.name}`);
      const handleProgress = (ev, file) => {
        if (!ev.lengthComputable) {
          return;
        }
        // 计算进度,并且以百分比形式展示
        const percent = Math.round((ev.loaded / ev.total) * 100);
        console.log(`<<< Loding ${file.name}, progress is ${percent}%`);
      };
    
      for (let file of files) {
        const reader = new FileReader();
        reader.onloadstart = ev => handleLoadStart(ev, file);
        reader.onprogress = ev => handleProgress(ev, file);
        reader.readAsArrayBuffer(file);
      }
    }
    
    document
      .querySelector("#files")
      .addEventListener("change", handleFileSelect, false);
    大文件分片读取

    在对于超大文件,一般采用分片上传的思路解决。文章开头有讲到,File 是 Blob 的一个特例。而 Blob 上有一个slice 方法,通过它,前端就可以实现分片读取大文件的操作。

    为了方便说明,请先准备好一个 txt 文件,文件内容就是:hello world

    示例代码如下,代码中只读取前 5 个字节,由于每个英文字母占 1 个字节,所以打印结果应该是“hello”。

    function handleFileSelect(event) {
      let { files } = event.target;
      if (!files.length) {
        return;
      }
      // 为了方便说明,这里仅仅读取第一个文件
      const file = files[0];
      // 读取前5个字节的内容
      const blob = file.slice(0, 5);
    
      const reader = new FileReader();
      // 控制台输出结果应该是:hello
      reader.onload = ev => console.log(ev.target.result);
      reader.readAsText(blob);
    }
    
    document
      .querySelector("#files")
      .addEventListener("change", handleFileSelect, false);
    拖拽上传

    和前面所述的 File API 相关是完全一样的。唯一需要特殊处理的是文件对象的获取入口改变了。对于标签,监听 onchange 事件,FileList 存放在 event.target.files 中;对于拖拽操作,FileList 存放在拖拽事件的回调函数参数里,通过 event.dataTransfer.files 访问即可。

    需要修改一下 html 代码:

    
    
      
      
    
    
      

    脚本文件的代码如下:

    function handleDropover(event) {
      event.stopPropagation();
      event.preventDefault();
    }
    
    function handleDrop(event) {
      event.stopPropagation();
      event.preventDefault();
      /***** 访问拖拽文件 *****/
      const files = event.dataTransfer.files;
      console.log(files);
      /**********/
    }
    
    const target = document.querySelector("#container");
    target.addEventListener("dragover", handleDropover);
    target.addEventListener("drop", handleDrop);
    后端相关

    后端相关超出了本文的讨论范围,可以参考这篇文章。

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/105889.html

    相关文章

    • 浏览器端下载那些事

      摘要:三浏览器方式相信大家对这个对象也不太陌生,它是标准里的一个二进制数据对象,可以与对象配合,进行文件的下载。其实这样一个简单的,就可以实现浏览器端自己的下载了。 一、背景 最近写了一个react的组件,用来做文件导出。环境是ie10+。细一点说,就是 1、读取form里的数据 2、向服务端发请求,并下载文件;要求拿到请求状态,如果出错及时反馈给用户。 第一个需求,我们借用了jquer...

      3fuyu 评论0 收藏0
    • [练习]JS鼠标拖拽(DnD)操作

      拖放(Drag and Drop,DnD)操作因为涉及到与底层OS的结合,所以是较为复杂的交互操作。 这里先实现一个简单的拖拽图片到浏览器显示到操作, 主要用到了HTML5中的FileAPI: 先上DEMODnD demo 需要注意的是浏览器通过取消相应的拖拽事件来表明它对该事件有兴趣, 比如通过取消dragover来表明浏览器已经做好准备接受进一步的拖拽,接着说dragend最后到drop,dr...

      jsummer 评论0 收藏0
    • Vue2.0利用vue-resource上传文件到七牛

      摘要:年底,公司项目番茄表单的前端部分,开始了从传统的到的彻底重构。上传流程图不重要看文字事件触发后,先去如果是图片,可以同时通过以及将图片预览在页面上后台请求七牛的上传,将拿到的和以及通过传递过来的一起到中。 关于上传,总是有很多可以说道的。16年底,公司项目番茄表单的前端部分,开始了从传统的jquery到vue 2.0的彻底重构。但是上传部分,无论是之前的传统版本,还是Vue新版本,都是...

      lcodecorex 评论0 收藏0

    发表评论

    0条评论

    最新活动
    阅读需要支付1元查看
    <