整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

「JS进阶」 HTML5 之文件操作(file)

在 HTML 文档中 <input type="file"> 标签每出现一次,一个 FileUpload 对象就会被创建。

该元素包含一个文本输入字段,用来输入文件名,还有一个按钮,用来打开文件选择对话框以便图形化选择文件。

该元素的 value 属性保存了用户指定的文件的名称,但是当包含一个 file-upload 元素的表单被提交的时候,浏览器会向服务器发送选中的文件的内容而不仅仅是发送文件名。

当用户选择或编辑一个文件名,file-upload 元素触发 onchange 事件句柄。

看个简单例子:

[html]view plaincopy

<!-- oscar999  -->  
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
<html>  
  <head>  
  <meta http-equiv="content-type" content="text/html; charset=utf-8">  
  <meta name="author" content="oscar999">  
  <title></title>  
  <script>  
  function  handleFiles(files)  
  {  
    if(files.length)  
    {  
       var file = files[0];  
       var reader = new FileReader();  
       reader.onload = function()  
       {  
           document.getElementById("filecontent").innerHTML = this.result;  
       };  
       reader.readAsText(file);  
    }  
  }  
  </script>  
  </head>  
  <body>  
  <input type="file" id="file" onchange="handleFiles(this.files)"/>  
  <div id="filecontent"></div>  
  </body>  
</html>  

这里读取一个文件, 显示在div 中。

(在IE8 中 无效, this.files 无法读取文件。这个属于HTML5 的特性)

当选择了一个文件时,就会把包含这个文件的列表(一个FileList对象)作为参数传给handleFiles()函数了。这个FileList对象类似一个数组,可以知道文件的数目,而它的元素就是File对象了。从这个File对象可以获取name、size、lastModifiedDate和type等属性。把这个File对象传给FileReader对象的读取方法,就能读取文件了。



HTML5 Drag and Drop File

Html5 支持的File 的操作不仅仅是文件的选择,

在HTML5 之前需要使用 Applet 和 SilverLight 才能达到的文件拖拽功能,在HTML5 中也能轻松的实现,

看代码:

[html]view plaincopy

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
<html>  
  <head>  
  <meta http-equiv="content-type" content="text/html; charset=utf-8">  
  <meta name="author" content="oscar999">  
  <title></title>  
  </head>  
  <body>  
    <div id="dropbox"> Drop Here </div>  
    <div id="filecontent"></div>  
    <script>  
      var dropbox = document.getElementById("dropbox");    
      dropbox.addEventListener("dragenter", dragenter, false);    
      dropbox.addEventListener("dragover", dragover, false);    
      dropbox.addEventListener("drop", drop, false);     
    function dragenter(e) {    
        e.stopPropagation();    
        e.preventDefault();    
    }    
    function dragover(e) {    
        e.stopPropagation();    
        e.preventDefault();    
    }  
    function drop(e) {    
        e.stopPropagation();    
        e.preventDefault();     
        var dt = e.dataTransfer;    
        var files = dt.files;  
        if(files.length)  
        {  
           var file = files[0];  
           var reader = new FileReader();  
           reader.onload = function()  
           {  
               document.getElementById("filecontent").innerHTML = this.result;  
           };  
           reader.readAsText(file);  
        }  
    }   
    </script>  
  </body>  
</html>  

这里通过事件对象的 dataTransfer 可以得到文件。



读取文件内容

在第一个例子中, 我们使用 FileReader类来读取文件的内容,

在 W3C 草案中,File 对象只包含文件名,文件类型等只读属性;FileReader用于内容读取和监控读取状态。

(在firefox 中, 可以直接使用 var fileBinary = file.getAsBinary(); 读取文件的二进制源码)

FileReader提供的方法包括:

1. readAsBinaryString

2. readAsDataURL

3. readAsText

4. abort

.........


以下,举一个 使用 FileReader 将用户选择的图片不通过后台即时显示出来的例子。

[html]view plaincopy

function handleFiles(files){  
    for (var i = 0; i < files.length; i++) {  
        var file = files[i];  
        var imageType = /image.*/;  
        if (!file.type.match(imageType)) {  
            continue;  
        }  
        var img = document.createElement("img");  
        img.classList.add("obj");  
        img.file = file;  
        preview.appendChild(img);  
        var reader = new FileReader();  
        reader.onload = (function(aImg){  
            return function(e){  
                aImg.src = e.target.result;  
            };  
        })(img);  
        reader.readAsDataURL(file);  
    }  
}  



同后端的交互

在一般的HTML 中,使用方式是把file input 放在form 中, 以POST 方式把文件传递到后端。

在 HTML5 中, 也可以通过 FileReader 的 readAsBinaryString 方法读取到文件的二进制码,然后通过 XMLHttpRequest 的 sendAsBinary 方法将其发送出去。

[javascript]view plaincopy

var xhr = new XMLHttpRequest();  
xhr.open("POST", "url");  
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');  
<pre code_snippet_id="422893" snippet_file_name="blog_20140709_4_2106578" class="sh_javascript sh_sourceCode" name="code">xhr<span class="sh_symbol">.</span><span class="sh_function">sendAsBinary</span><span class="sh_symbol">(</span>binaryString<span class="sh_symbol">);</span></pre><br>  

ileAPI

介绍

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

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

准备工作

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

<input type="file" id="files" multiple />
<div id="list"></div>
<div id="images"></div>
<!-- File API相关操作写在了script.js中 -->
<script src="./script.js"></script>

检测兼容性

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

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

监听文件选择

对于 typefile 类型的 <input> 标签,在选择文件的时候,会触发 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(
 `
 <li>
 <strong>${file.name}</strong>
 (${file.type || "n/a"}) -
 ${file.size} bytes
 </li>
 `
 );
 }
 document.querySelector("#list").innerHTML = `<ul>${innerHTML.join("")}</ul>`;
}

FileReader

读取文件-FileReader

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

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

  • onload: 文件读取完成
  • onloadstart:文件上传开始
  • onprogress : 文件上传中触发

和Image类似,在读取文件之前,需要先绑定事件处理。它读取操作有:readAsArrayBufferreadAsDataURLreadAsBinaryStringreadAsText。传入的参数就是 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);

slice

大文件分片读取

在对于超大文件,一般采用分片上传的思路解决。文章开头有讲到,FileBlob 的一个特例。而 Blob 上有一个 slice (https://developer.mozilla.org/zh-CN/docs/Web/API/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 相关是完全一样的。唯一需要特殊处理的是文件对象的获取入口改变了。对于 <input> 标签,监听 onchange 事件,FileList 存放在 event.target.files 中;对于拖拽操作,FileList 存放在拖拽事件的回调函数参数里,通过 event.dataTransfer.files 访问即可。

需要修改一下 html 代码:

<!DOCTYPE html>
<head>
 <meta charset="UTF-8">
 <style>
 #container {
 width: 300px;
 height: 300px;
 border: 3px dotted red;
 }
 </style>
</head>
<body>
 <div id="container"></div>
 <script src="./script.js"></script>
</body>
</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://github.com/purplebamboo/blog/issues/17)。

版权

作者:心谭

链接:https://juejin.im/post/5d35af63e51d454fa33b199e

著作权归作者所有。

关注

感谢阅读,如果这篇文章对你又帮助,记得 点赞收藏转发 哟。

期待下次与你相遇 :)

tml5超大文件上传和断点续传的实现,html5超大文件上传的实现,html5超大文件上传解决方案,html5超大文件上传思路,html5超大文件上传实例,html5超大文件上传源码,html5超大文件分片上传,html5超大文件分块上传,html5超大文件加密上传,html5文件夹上传,


要求操作便利,一次选择多个文件和文件夹进行上传;

支持PC端全平台操作系统,Windows,Linux,Mac

支持文件和文件夹的批量下载,断点续传。刷新页面后继续传输。关闭浏览器后保留进度信息。

支持文件夹批量上传下载,服务器端保留文件夹层级结构,服务器端文件夹层级结构与本地相同。

支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验;

支持文件夹上传,文件夹中的文件数量达到1万个以上,且包含层级结构。

支持断点续传,关闭浏览器或刷新浏览器后仍然能够保留进度。

支持文件夹结构管理,支持新建文件夹,支持文件夹目录导航

交互友好,能够及时反馈上传的进度;

服务端的安全性,不因上传文件功能导致JVM内存溢出影响其他功能使用;

最大限度利用网络上行带宽,提高上传速度;

对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传

从上传的效率来看,利用多线程并发上传能够达到最大效率。

文件上传页面的前端可以选择使用一些比较好用的上传组件,例如百度的开源组件WebUploader,这些组件基本能满足文件上传的一些日常所需功能,如异步上传文件,文件夹,拖拽式上传,黏贴上传,上传进度监控,文件缩略图,甚至是大文件断点续传,大文件秒传。

在web项目中上传文件夹现在已经成为了一个主流的需求。在OA,或者企业ERP系统中都有类似的需求。上传文件夹并且保留层级结构能够对用户行成很好的引导,用户使用起来也更方便。能够提供更高级的应用支撑。


下载示例:

https://gitee.com/xproer/up6-jsp-eclipse/tree/6.5.40/




工程



NOSQL

NOSQL示例不需要任何配置,可以直接访问测试



创建数据表

选择对应的数据表脚本,这里以SQL为例




修改数据库连接信息


访问页面进行测试



文件存储路径

up6/upload/年/月/日/guid/filename






相关问题:
1.javax.servlet.http.HttpServlet错误
2.项目无法发布到tomcat
3.md5计算完毕后卡住
4.服务器找不到config.json文件

相关参考:

文件保存位置


源码工程文档:https://drive.weixin.qq.com/s?k=ACoAYgezAAw1dWofra

源码报价单:https://drive.weixin.qq.com/s?k=ACoAYgezAAwoiul8gl



OEM版报价单:https://drive.weixin.qq.com/s?k=ACoAYgezAAwuzp4W0a

产品源代码:https://drive.weixin.qq.com/s?k=ACoAYgezAAwbdKCskc
授权生成器:https://drive.weixin.qq.com/s?k=ACoAYgezAAwTIcFph1