abbshr / abbshr.github.io

人们往往接受流行,不是因为想要与众不同,而是因为害怕与众不同

Home Page:http://digitalpie.cf

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

大话文件上传的前前后后

abbshr opened this issue · comments

web标准发展至今,浏览器能干的事越来越多了,各种新鲜技术的出现无疑扩展了客户端app的职能,比如说上一篇po提到的ArrayBufferBlobURL等。这个寒假我写了一個文件拖拽上传的脚本。当时我想毕竟这是一个正经的web应用,就算是前端也一定要做到尽量无可挑剔。因此,新鲜技术终于得以利用。

我们暂且不考虑应用架构和代码风格,怎么做才能无可挑剔呢?答案就是把严格控制工作流程的每一环节:从选择文件到解析文件再到文件上传最后到善后工作。(当然,如果人家禁用JavaScript所有的工作都前功尽弃了。不过为了弥补这一不足,我们在后面会有一个小小的解决方案。)

获取文件

文件的获取有两个途径:A.上传按钮选择; B.拖拽选择。这没啥可说的,只是为了方便拖放操作额外写了一个拖放事件库,代码就不贴了。

类型检测

关键是文件解析。或许你要问“上传个文件给服务器,为毛在前端弄那么复杂?”,还是那句话“为了做到应用的完整与无可挑剔”。上传时我们考虑之允许指定格式的文件,如“PDF、DOC、JPEG、CAD”等等能形成人类可读文档的文件,如果文件格式不满足要求则拒绝上传。

要想检测文件类型,至少得把文件头读出来。怎么做?要是以前还真就麻烦了,可是现在web标准给出了FileReader,使用它可以将二进制的blob文件进行转化处理。还记得ArrayBuffer吗?这里我们就用他们来完成文件头的获取:

var typeVerify = function (file) {

    var filereader;

    var token = true;

    // 创建一个FileReader
    filereader = new FileReader();

    filereader.onload = verify;

    // 以异步方式将file转成ArrayBuffer(同步方式只有在Worker线程中才可用)
    filereader.readAsArrayBuffer(file.slice(0, 4));

    function verify(e) {

        var buffer = e.target.result;

        // 创建一个DataView对象用来按字节处理结果
        var dataView = new DataView(buffer);

        // false以大端次序读取2字节(true为小端次序)
        var fh = dataView.getUint16(0, false);

        // 如果得到的值不在fileHeader里
        if (fileHeader.indexOf(fh) == -1) token = false;

        return token;

    }
};

OK,我们已经把过程封装到函数,用来检测来自用户输入的文件是否满足要求。

异步上传

WebSocket、Ajax可供选择,鉴于长连接开销问题,选择Ajax吧。新的XMLHttpRequest 2标准中规定了用于文件上传的接口,可用于二进制文件上传以及监测文件上传进度等。

var asyncUpload = function (binfile) {

    var xhr = new XMLHttpRequest();

    // 注册该事件可用于获取上传进度
    xhr.upload.onprogress = updateProcess;

    // 上传成功
    xhr.upload.onload = success;

    // 上传结束(无论成败)
    xhr.upload.onloadend = end;

    xhr.upload.onerror = error;

    // 服务器返回结果
    xhr.onreadystatechange = watchState;

    xhr.open('POST', '/upload');

    // 设置响应类型
    xhr.responseType = 'blob';

    xhr.send(binfile);
};

最后一步

大体的架构设计算结束了,最后一步是什么呢?

对了,我们折腾了半天,要是用户不买账,把JavaScript给禁用了或给你改了咋办???……

这个做法着实让人气愤,为了防止自己的成果功亏一亏,我考虑了这种办法:

  1. 纯粹JS型页面。即整个页面都是由JS生成,让你禁用~
  2. 服务器检测请求的Header,通常web浏览器最差也得通过表单实现上传,表单上传的缺点之一是没法控制请求头。那么我们就用ajax添加个特殊的请求头。
  3. 把写好的JS文件进行压缩(这个普遍都有,最好弄得面目全非让人看了就想吐。。)

不过呢,尽管是这样做,还是有很多漏洞的,仅仅能打消一下hacking的念头,如果用户真的想整整你,办法还是多的去的。