08月02, 2016

HTML5文件上传及Node的excel相关处理

最近在做类似OA的功能,有一个导入excel的功能。如图所示:

图

简单地说,就是支持将文件拖拽到指定区域,或者点击按钮,上传excel文件,通过后端处理返回列表数据,然后实现一个伪分页。

先说界面

上传按钮的处理:

  • 将type="file"的input盖在按钮的元素上,有一个透明度,还有z-index的处理。兼容性比较好
  • 按钮和input分离,input隐藏,当点击按钮时,触发input的click事件。这种方式在高级浏览器是没有问题的,但在IE6 7下会得到一个“浏览器拒绝访问”的错误。别问我为什么记得这么牢,曾经有一个血一般的教训。(不过话说回来,这年头也没什么人管IE6 7的兼容了,所以一般第一种就OK了)

将文件拖拽到指定区域

我们都知道,直接将某个image图片或者某个excel文件,拖到浏览器界面,结果可能是打开图片,或者下载文件。

所以第一步先要禁用默认事件,通过event.preventDefault()来处理

document.addEventListener("dragleave", function(ev) {
    ev.preventDefault();
}, false);

document.addEventListener("drop", function(ev) {
    ev.preventDefault();
}, false);

document.addEventListener("dragenter", function(ev) {
    ev.preventDefault();
}, false);

document.addEventListener("dragover", function(ev) {
    ev.preventDefault();
}, false);

随后,我们对可拖拽的区域监听drop事件

//得到上传区域的DOM
var dragDiv = document.querySelector(".dragArea"); 
//监听drop事件
dragDiv.addEventListener("drop", function(ev) {
    ev.preventDefault();
    //通过e.dataTransfer.files拖拽事件传递的文件信息,获取本地文件列表信息。
    var fileList = ev.dataTransfer.files;
    if (fileList.length == 0) {
        return false;
    }
    //这里开始处理上传的操作
    upload(fileList[0]);

}, false);

upload这个方法,等下会说,因为点击上传按钮也是会调用这个方法的

点击按钮

//得到input对象,即input type="file"的DOM对象
var chooseFile = document.getElementById("chooseFile");
chooseFile.addEventListener("change", function(){

    upload(this.files[0]);

}, false);

upload上传事件

function upload(file) {

    var xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
            var response = xhr.responseText;
            try {

                var json = JSON.parse(response);
                console.log(json.result.length);

            } catch (e) {
                console.log("error");
            }
        }
    }

    xhr.open("post", "/upload", true);
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

    //为了方便表单处理,HTML 5新增了一个FormData对象,可以模拟表单。
    var fd = new FormData();
    fd.append("myFile", file);

    xhr.send(fd);
}

这里用到了xhr2的新特性,更多可以参考:XMLHttpRequest Level 2 使用指南

进度条功能实现

var last = 0;
xhr.upload.onprogress = function(evt){  
    //通过事件对象侦查  
    //该匿名函数表达式大概0.05-0.1秒执行一次  
    //console.log(evt);  
    //console.log(evt.loaded);  //已经上传大小情况  
    //evt.total; 附件总大小  
    var percent = Math.round(event.loaded*100/event.total);  
    var diff = event.loaded - last; //得到当前剩余的上传量
        last = event.loaded;
}

在实际过程中,可能还会涉及显示文件大小的单位,可以通过以下函数进行处理:

//对size进行单位的转换
function handleSize(size){
    var fileSize = 0;
    if(size > 1024*1024){
        fileSize = Math.round(size/(1024*1024))+"MB";
    }else{
        fileSize = Math.round(size/(1024))+"KB";
    }
    return fileSize;
}

Node的文件处理

单纯只是上面的内容,未免显得太简单了,所以干脆就来一个node的文件处理。

有一个formidable的npm模块,比较擅长处理文件上传这一块。

var http = require("http"),
    url = require("url"),
    fs = require("fs"),
    util = require("util"),
    formidable = require("formidable");

http.createServer(function(req,res){
    var urlObj = url.parse(req.url,true);
    var pathname = urlObj.pathname;
    if(pathname=="/upload"){
        //创建表单上传
        var form = new formidable.IncomingForm();
        //设置编辑
        form.encoding = "utf-8";
        //设置文件存储路径
        form.uploadDir = "temp/";
        //保留后缀
        form.keepExtensions = true;

        form.parse(req, function(err, fields, files) {

            //这里我们就能得到上传了的文件
            var file = files["myFile"];

            res.writeHead(200, {"content-type": "text/html;charset:utf-8"});
            res.write("输出你要的结果");
            res.end();
        });
    }
}).listen(8090,function(){
    console.log("server started");
});

比较简单,需要注意的是这里的uploadDir目录一开始就必须存在,不然会报错。

必须设置它的原因是,mac下它的默认临时存储目录是在/var下面的,如果我们要进行文件重命名(即移动文件到新的位置),会报权限不够。当然可以把var目录权限修改一下,但我不推荐。

在得到file之后,其实可以往前端输出一些信息,比如说输出文件的URL什么的。

Node的xsl处理

我用的是xlsx这个npm模块。

假如excel文件是这样的:

excel文件

var XLSX = require("xlsx");

//file.path是excel文件地址
var workbook = XLSX.readFile(file.path),
    firstSheetName,
    result = {};

//每个excel表会有多个sheet,就是左下角的名称,什么sheet1、sheet2等。
workbook.SheetNames.forEach(function(sheetName, index) {
    if (index == 0) {
        firstSheetName = sheetName;
    }
    console.log(sheetName);
    worksheet = workbook.Sheets[sheetName];
    result[sheetName] = XLSX.utils.sheet_to_json(worksheet);
});

console.log(result[firstSheetName])

打印结果如下图所示:

打印结果

也就是说它会帮你将表头和数据一一对应起来,形成一个json的数组对象。那么我们可以将这个结果write到前端去。

在coding的过程中,我遇到了文件乱码的问题,需要设置一下response的编码为UTF8

还有很多...

上面只是很简单的excel输出处理,在实际的工作中,可能还有合并单元格这些。等将来遇到了,我再来填坑吧!

对前端来说,当excel的条数有几百条时,需要实现一个伪分页。

这时候如果使用mvvm框架,就比较容易,只要处理请求回来的数据,类似:

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
var list = array.slice(0, 10)

只要slice一下就行了,第一页0-10,第二页11-20,这里就不展开了。

参考文章

本文链接:www.my-fe.pub/post/h5-file-upload.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。