纯 js 来实现大文件的分片上传

梦康 2018-02-11 00:00:00 1254

已有轮子

http://fex.baidu.com/webuploader/

原理

前端通过file.slice对文件做切割,后端拼接。

简单实现

用别人的代码做了下实验,https://www.cnblogs.com/fjzhang/p/7227401.html

分片上传,这样就不用担心服务器请求超时了。

前端页面

<div class="layui-container">
    <input type="file" id="file" multiple required>
    <br>
    <div class="layui-progress layui-progress-big" lay-filter="demo">
        <div class="layui-progress-bar layui-bg-green"></div>
    </div>
    <br>
    <button class="layui-btn btnFile">立即提交</button>
    <script type="text/javascript">
        var element = layui.element;
        var time = new Date().getTime();
        var upload = function (file, num) {
            var formData = new FormData();
            var blockSize = 1024 * 1000;
            var blockNum = Math.ceil(file.size / blockSize);
            var nextSize = Math.min(num * blockSize, file.size);
            var fileData = file.slice((num - 1) * blockSize, nextSize);
            formData.append("file", fileData);
            formData.append("fileName", file.name + time);
            $.ajax({
                url: "/admin/op/seoAsk?action=fileUpload",
                type: "POST",
                data: formData,
                processData: false,
                contentType: false,
                success: function (responseText) {
                    element.progress('demo', ((num * 100) / blockNum) + '%');
                    if (file.size <= nextSize) {
                        layer.msg("上传成功");
                        return;
                    }
                    upload(file, ++num);//递归调用
                }
            });
        };
        $(".btnFile").click(function () {
            var file = $("#file")[0].files[0];
            upload(file, 1);
        });
    </script>
</div>

后端实现

$file = isset($_FILES['file']) ? $_FILES['file']:null; //分段的文件

if (!isset($_POST['fileName'])) {
    echo 'failed';
}

$filename = str_replace(["..","/"."\\"],"",$_POST['fileName']);

$content = file_get_contents($file['tmp_name']);
if (!file_put_contents($name, $content, FILE_APPEND)) {
    echo 'failed';
}

优化与改进

  1. 鉴权
  2. 断点续传
  3. 多线程上传

我设想的一个思路:

第一步,客户端发送身份+文件 md5,以及该文件的总分片数。身份可以是登录用户的 uid。
第二步,服务端返回该文件已经上传了的分片索引数组,这样就可以可以实现断点续传。我们设定分片大小是固定的,如果文件 md5 不变,那么它分片的结果也不会变。
第三步,前端根据后端返回的分片索引来并行的上传文件,而不需要像上面那样同步递归。
第四步,服务端收到分片根据客户端身份和文件 md5来进行文件合并。当分片到达首次传输过来的该文件对应的总分片数时才合并。