文件分块上传


只有支持 XHR 文件上传和 Blob API 的浏览器才允许文件分块上传,其中包括 Google Chrome 和 Mozilla Firefox 4+。

客户端安装


要将大文件分成小块上传,需要设置 maxChunkSize 配置项(参阅配置项手册)为每一块的大小(单位,字节):

$('#fileupload').fileupload({
    maxChunkSize: 10000000 // 10 MB
});

要在 Mozilla Firefox 4 - 6 中进行分块上传(FF7 之前支持 XHR 上传的版本),还必须将 multipart 配置项设置为 false - 配置项手册maxChunkSize 有相关说明。

服务端安装


参考 PHP 上传处理示例,其涵盖了支持开箱即用的分块上传代码。

为了支持分块上传,服务端程序得利用 Content-Range 响应标头,本插件传输每一分块时都会附带该标头。

分块上传工作原理


如果 maxChunkSize 设置为大于 0 的整数,则插件会将文件分割成大小不大于 maxChunkSize 的多个 blob 分块,并将这些 blob 按顺序提交到上传 URL。

blob 的范围信息通过 Content-Range 标头传输。译者注:例,一个 3922 大小的文件拆成 2 个 blob,第一个 header 为 Content-Range: bytes 0-2000/3922,那么第二个分块则是 Range: bytes=2001-3922,其对应的 header 为 Content-Range: bytes 2001-3922/3922。

文件名通过 Content-Disposition 标头传输。

回调函数


分块上传会触发和正常上传一样的回调函数,例如只有在最后一个 blob 成功上传后才触发 done 回调(请参阅 API 指南)。

分块上传会触发额外的回调,可用于追踪单个分块上传的事件:

$('#fileupload').fileupload({maxChunkSize: 100000}) 
    .on('fileuploadchunkbeforesend', function (e, data) {})
    .on('fileuploadchunksend', function (e, data) {})
    .on('fileuploadchunkdone', function (e, data) {})
    .on('fileuploadchunkfail', function (e, data) {})
    .on('fileuploadchunkalways', function (e, data) {});
注意:回调(例如 successerrorcomplete)作为 $.ajax 配置项的一部分会在每次 AJAX 请求中被调用,包括每个独立分块上传的请求。

跨域分块上传


默认情况下,浏览器禁用跨域文件上传的所有 header,除非有明确定义下述服务端响应 header:

Access-Control-Allow-Headers Content-Type, Content-Range, Content-Disposition

断点续传


使用 uploadedBytes 配置项(请参阅配置项手册),可以恢复已中止的上传:

$('#fileupload').fileupload({
    maxChunkSize: 10000000, // 10 MB
    add: function (e, data) {
        var that = this;
        $.getJSON('server/php/', {file: data.files[0].name}, function (result) {
            var file = result.file;
            data.uploadedBytes = file && file.size;
            $.blueimp.fileupload.prototype
                .options.add.call(that, e, data);
        });
    }
});

上面代码复写了 add 回调,并向服务器发送了一个带有当前文件名的 JSON 请求。如果已存在该文件名的文件,则服务器响应文件信息,包括文件大小(设置为 uploadedBytes 配置项)。

如果设置了 uploadedBytes,则插件仅将文件的剩余部分作为 blob 上传。

自动续传

以下代码段基于前面的代码实现了自动续传功能:

$('#fileupload').fileupload({
    /* ... settings as above plus the following ... */
    maxRetries: 100,
    retryTimeout: 500,
    fail: function (e, data) {
        // jQuery Widget Factory uses "namespace-widgetname" since version 1.10.0:
        var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'),
            retries = data.context.data('retries') || 0,
            retry = function () {
                $.getJSON('server/php/', {file: data.files[0].name})
                    .done(function (result) {
                        var file = result.file;
                        data.uploadedBytes = file && file.size;
                        // clear the previous data:
                        data.data = null;
                        data.submit();
                    })
                    .fail(function () {
                        fu._trigger('fail', e, data);
                    });
            };
        if (data.errorThrown !== 'abort' &&
                data.uploadedBytes < data.files[0].size &&
                retries < fu.options.maxRetries) {
            retries += 1;
            data.context.data('retries', retries);
            window.setTimeout(retry, retries * fu.options.retryTimeout);
            return;
        }
        data.context.removeData('retries');
        $.blueimp.fileupload.prototype
            .options.fail.call(this, e, data);
    }
});

如果上传失败,上面的代码会在检索到已上传字节后自动恢复文件上传。

为了防止无限循环,代码中使用了 maxRetries 限制重试次数。

retryTimeout 定义了恢复文件上传之前的等待时间(单位,毫秒)。等待时间每重试一次增加一次 retryTimeout 时长。

清理失败残余文件


如果你的界面中不提供恢复上传的选项,那你可能会希望可以删除服务端中途中断的文件。推荐的做法是将这个步骤放在服务端去执行,例如通过 cron 计划任务去定时清理未完成的文件。

不过,假如你想一个快速方案,可以在分块上传失败时发送 DELETE 请求(例如,当用户中止上传时):

$('#fileupload').fileupload({
    maxChunkSize: 10000000, // 10 MB
    fail: function (e, data) {
        $.ajax({
            url: 'server/php/',
            dataType: 'json',
            data: {file: data.files[0].name}
            type: 'DELETE'
        });
    }
});

扩展