Open 2betop opened 10 years ago
@XGHeaven 你最好使用js调试一下,看看文件传入MD5计算方法时候的大小,起止位置等属性,如果是图片还要看看是不是压缩了。
@beyond290239 你的代码里面没有使用deferred ,后台合并成则deferred.resolve(); 然后return deferred.promise();
/**
* method:after-send-file
* 在所有分片都上传完毕后,且没有错误后request,用来做分片验证,此时如果promise被reject,当前文件上传会触发错误。
* para:file: File对象
*/
uploader.register({
'after-send-file' : 'chunkUploadFinish'
}, {
chunkUploadFinish : function(file) {
insertLog("<br>"+moment().format("YYYY-MM-DD HH:mm:ss")+" after-send-file chunkUploadFinish:文件 "+file.name + " 分片上传完成;");
//var me = this;
//var owner = this.owner;
var deferred = $.Deferred();
var fileMd5 = file.wholeMd5;
var chunks = file.chunks;
if(chunks > 1){//TODO 向server发送文件合并请求,根据结果决定文件上传成功与否
$('#' + file.id).find('span.state').text("合并文件...");
$.ajax({
cache : false,
async:true,
type : "post",
dataType : "json",
url : baseUrl + "/fileUpload/fileMerge",
data : {
fileMd5 : fileMd5,
isShared : $("#isShared").val(),
fileType : $("#fileType").val(),
ext:file.ext //文件扩展名称
},
success : function(result) {
if (result.result) {
insertLog("<br>"+moment().format("YYYY-MM-DD HH:mm:ss")+" after-send-file chunkUploadFinish:文件 "+file.name + " 合并文件成功");
$('#' + file.id).find('span.state').text("合并文件成功");
insertLog("<br>"+moment().format("YYYY-MM-DD HH:mm:ss")+" after-send-file chunkUploadFinish:文件 "+file.name + " server响应:"+result.msg);
deferred.resolve();
} else {
insertLog("<br>"+moment().format("YYYY-MM-DD HH:mm:ss")+" after-send-file chunkUploadFinish:文件 "+file.name + " server响应:"+result.msg);
$('#' + file.id).find('span.state').text("合并文件失败");
deferred.reject();
}
}
});
}else{
deferred.resolve();
}
return deferred.promise();
}
});
@sky20054122 谢谢!就是这个问题,已解决
@sky20054122 我测试了一下,在Flash下Md5验证更快,但是当我测试一个文件在2G左右的时候,Md5不能验证,而且还有一个很奇怪的现象是我上传一个1.1G的文件,Flash下可以进行Md5验证,但是不能上传,一直卡在上传的地方,请问一下,有没有解决的方案?
@sky20054122 不想使用Js验证的原因是,验证太慢
@sky20054122 我把runtimeOrder: 'flash',调整为flash
@beyond290239 应该优先使用js验证,不支持html5的浏览器才使用flash上传作为补充; 因为flash是把整个文件读进内存才能计算MD5,大文件上传容易导致浏览器消耗内存过大,系统反应慢;
我使用html5模式计算MD5和快,不管是chrome,firefox和ie都必须关掉F12或者firebug调试,尽量不要在console输出;这样计算MD5就很快,理论上应该比flash快多了。
html5计算速度大概是15M/秒
@sky20054122 那这样意味着,在ie7 8 9中大文件上传是不支持的?
低版本IE只能使用flash上传,传大文件虽然可以,但是很耗内存;
MD5的计算可以只计算文件的一部分(例如取文件的首位个一部分计算MD5),前后台一致,可以很快计算出该文件是否已经存在
@sky20054122 我设置为runtimeOrder: 'flash'这样的模式上传,并且我的内存是8G,所有的物理资源都够,但是1.1G的文件能做Md5校验,但是不能上传,做完了md5后就卡在哪里,同时,上传2G的文件,连Md5都不能校验,目的就是测试一下ie中我能否设置,以及我要提供的最大上传文件大小
@beyond290239 我刚才测试了一下flash模式下,大文件上传,chrome和firefox都没有问题,在IE11下,计算MD5都不动,目前不清楚原因。
@sky20054122 恩,chrome和firefox都没有问题,但是我测试的是800M左右,但是上了1G的就不行,就和你说的跟IE中一样
怎么才能停止上传呢?我使用stop方法,不能停止md5扫描。
@sky20054122大神
@sky20054122大神
@wwg88888888 目前调用两种停止,都没有停止计算MD5 ; 分片上传的暂停和继续会丢掉缓存中三个分片的部分,导致最终上传的分片不足, 如果你需要停止MD5计算,要@2betop 添加此功能才行。我只是使用webuploader ,没有参与开发!
@2betop md5 能停止扫描码?2G的文件,选错了,这停止都停止不掉
@2betop md5 能停止扫描码?2G的文件,选错了,这停止都停止不掉
你这个妙传文件的时候,按照你的思路,先算md5,然后与服务端对比,但是如果服务端没有的话,因为你的流数据已经读过一次,不能重复再读,你怎么把数据再传到服务器。小文件可以在算md5的时候,这样数据变成byte[]数组是可以得,但是大文件肯定不行,不知道你这个大文件妙传能支持多大。或者还是有别的解决方案。
我是利用那个hash值来进行比对,算md5太慢了, 我自己改了三处代码 , 就能实现前端的断点续传的支持。 但现在有个新的问题,就是服务器端给我明明是有数据的, 但在uploadSuccess监听的事件中有时候却取不到服务端的值。为什么呢 ? 感觉像是在上传完成前就执行了uploadSuccess这个事件。
分片上传有两个问题: 1)md5校验时太慢了 2)后台多线程同步问题很复杂,怎么让多个分片按顺序写到同一文件中?
@lw394407679 webuploader是利用多线程上传, 而且上传时是没有顺序的, 所以我先将分块的文件放在一个独立的临时目录中, 当上传完成后,再进行合并。 而不是写到同一文件中(除非你将线程数改为1),
@lw394407679 你没遇到分片上传点击暂停又继续上传,会丢失分片的情况吗?
flash分片上传按理应该内存不会暴增因此我分为多个小片上传,为何不会释放,是不是flash问题?
关于断点续传与秒传的总结: 1、首先说说pupload这个插件,用过它的都知道,其实webuploader的功能与它有惊人的相似之处,但是pupload是不带md5验证的,得自己去计算。这几天在研究的过程中发现,pupload在HTML5下利用FileRead读取md5时是没有文件大小限制的,但是在flash下,flash实际上是调用的Moxie.swf接口。
这里着重说说flash的断点续传与秒传(同时也希望开发团队能够参考参考):
当文件大小在100M以下时:读取文件流然后利用spark-md5去计算出MD5是没有问题的。
那么,当文件在100M以上,甚至1G,10G,100G的时候怎么办呢?就算是HTML5模式下去计算这样的文件耗时也非常严重,有时候无法忍受了。
于是我想着能不能读取文件的开始字节+结束字节然后组合成 一个伪造的md5去进行验证呢?
可以想象一下,假设文件有351M,我以5M的区间去读取 0-5M的数据,然后346-351的数据,最终组合成一个MD5,这样在(99.9%)常规情况下,算出来的md5值也是唯一的,基本不会出现重复的。
于是我想:既然可以利用这种方式:那么应该在HTML5与Flash 下都能算出md5了,因为这样实际上我只读取了10M的内容。甚至这个值可以设置成1M一个区间,那么2M的内容算MD5,不会超过20ms。
但是:最后经过实现发现,pupload在html5时这样读取是没有任何问题,可以读取并算出md5值的,但是在flash下,超过100M文件时在读取的时候还是报错了。(这个我查阅资料说是flash fileread instance单个实例只支持100M的文件,所以读取会报错)
昨天我正在纠结这个问题的时候偶然发现国产大作 webuploader这款上传插件,于是信心满满的又开始测试了,webuploader自带md5计算方法
uploader.on('fileQueued', function (file) { uploader.md5File(file, 0, 5242880).progress(function (percentage) { $("#thelist").append("
等待上传...
' + '利用 uploader.md5File(file,start,end)方法,我们轻易可以计算出区间的md5,也可以计算完整的md5。
但是.....................在flash模式下(用ie8),又发现了相同的问题,在文件超过100m时同样计算不出md5值,并且假设文件是50g 直接 uploader.md5File(file) 与 uploader.md5File(file,0,"1mb") 所计算的耗时是一样的。
现在初步推测原因是flash的fileread instance 造成的,并且flash在读取区间数据时应该还是先把整个文件加载到内存,从内存中取得的,所以计算整个文件的md5与取一个区间是没有区别的,但是flash这块不了解,也不敢轻易改造源码。
希望webuploader开发团队能在这块上有个崭新的突破,那么做到真正的全兼容 断点续传、秒传也就不远了。
我感觉对文件内容做md5加密 ,不如对文件名,文件大小,文件修改时间等文件信息合并做一个hash值,这种情况发生冲突的概念也是相当小的。我做断点续传的秒传思路与原作者不同, 看了源代码:代码没有对每一块分块上传完成后的事件,若某个分块没有实际保存上,就坑了, 并且每一次分块上传都得去做ajax验证,而且skipfIle这个函数是跳过整个文件, 我做了些改造,能实现在整个文件开始上传前,我去获取已传上传完成的chunk, 然后从缺的分块开始上传,只要是上传过的分块就直接跳过。
楼上这种做法早在几年前用pupload试过,不适用,做出来毫无意义,打个比方用户A上传了一份SQL SERVER安装包,他电脑的时间,文件名等可能和 B用户电脑上的不相同,当B用户又上传一份SQL SERVER安装包时,你无法确定已经穿过了,做不了秒传,最多只能做针对“同一份文件”的断点续传。
不是毫无意义,也有一种情况用户在文件的内容后追加了一些内容, 用md5做加密, 得到的值还是不同的,实际上他们相似度可能99.99%, 所以就取决于 对“同一份文件”的定义了, 我这边的应用场景更多的是针对某一个用户的上传,他上次上传了55%,这次要接着传,我说的思路更多是“断”点“续”传,不是秒传。
分片上传功能已实现,但是暂停上传后再次触发上传方法时,上传进度又从0开始上传了
uploader.on( 'startUpload', function( file ) { uploader.md5File( file ) .then(function(val) { console.log('md5 result:', val); $md5 = val; }); }); 为什么会报错呢?加入列队后fileQueued能正常执行,但是startUpload状态确保错 求大师讲解
@beyond290239 @sky20054122 我遇到个这么问题,断点续传做分片上传的时候,我设置了4个线程一起上传,有的用户上传失败,很小一部分用户存在这个问题,偶发问题,查log是分4个线程后,分片文件总数小于四个,并发时有的上传请求就丢了,没有请求到server,导致上传失败。自己本地自测很多遍没有重现,怎么解? 有人遇到过类似的问题么,还有就是当分片文件总数小于线程数时会有无效的请求。即空参数的请求。
我遇到的情况是 有分块丢失的情况, 但不清楚是因为没有发起请求,还是服务端的问题。 我也是自己在本地自测测了很多次没有复现,所以后来我在一个文件上传成功之后 ,去 上传服务器 检测一下,判断是否真正上传成功。
用uploader.stop(file)暂停单个文件上传后,用uploader.upload(file)无法继续上传,是怎么回事?
14M的文件,用谷歌/火狐分片上传,好卡啊~整个上传完成差不多要五六分钟,是我哪里没有写对么?
<script type="text/javascript">
var GUID = WebUploader.Base.guid();//一个GUID
var uploader = WebUploader.create({
swf: '/Scripts/Plugins/webuploader-0.1.5/Uploader.swf',
server: '@Url.Action("Upload")',
pick: '#picker',
resize: false,
chunked: true,//开始分片上传
chunkSize: 2048000,//每一片的大小
formData: {
guid: GUID //自定义参数,待会儿解释
}
});
var $list = $("#thelist");
uploader.on('fileQueued', function (file) {
$list.append('<div id="' + file.id + '" class="item">' +
'<h4 class="info">' + file.name + '</h4>' +
'<p class="state">等待上传...</p>' +
'</div>');
});
uploader.on('uploadSuccess', function (file, response) {
$.post('@Url.Action("Merge")', { guid: GUID, fileName: file.name }, function (data) {
$list.text('已上传');
});
});
uploader.on('uploadProgress', function (file, percentage) {
console.log('Percentage:', percentage);
$("#progress-bar").width(percentage * 100 + "%");
});
$("#ctlBtn").click(function () {
uploader.upload();
});
</script>
@luoyehanfei 确实,现在需要兼容IE7/8/9支持,只有flash的方案了,但是flash计算md5确实太慢了。看NTKO的大附件上传非常的快,不知道是用了什么方案
@2betop @sky20054122 有其它办法解决吗?
IE7/8/9 计算MD5还是不兼容吗? 或者是我没找对方法?
怎么在外部添加个文件到上传队列呢?
@2betop 怎么在外部添加个文件到上传队列呢?
当我切换到IE9下时,选择文件按钮失效,我在webuploader.js 源码中调用flash的位置alert了一下,发现alert没反应,而在IE10和IE11下则没问题,因此确定flash根本没有加载,对于此问题楼主有没有解决之道
@2betop
uploader.register({
'before-send' : 'checkchunk'
}, {
checkchunk : function(block) {
用了上面的HOOK的before-send上传前分段检测MD5, 下面的方法的进度条显示percentage一直是0 uploader.on('uploadProgress', function (file, percentage) { console.log('Percentage:', percentage); $("#progress-bar").width(percentage * 100 + "%"); });
为什么我owner.md5File(file)后返回是undefined呢?
有一个问题 服务器如何得知这个文件的md5 难道把上传目录的所有文件全部md5 一遍吗 如果有多个上传目录呢
@diskrooms ,肯定是前端计算所有文件MD5值,然后发送到后端,与后端计算的MD5值匹配。要比较必须要有来源和目标。
var fileT = uploader.getFile(parentT) uploader.stop(fileT)
Cannot read property 'file' of undefined at HTMLSpanElement.filePause (uploadPage.html:203) 指向stop方法
@gkm1987 ,先确认var fileT = uploader.getFile(parentT)这一句有没有问题。
this.uploader.retry(continueFile);
我这里断网之后再连接调用upload方法或者retry并不续传,为什么?
因为这是小众需求,所以默认没有做在webuploader里面,而只是提供hook接口,让用户很简单的扩展此功能。
那么,都有哪些重要的hook接口呢?
before-send-file
此hook在文件发送之前执行before-file
此hook在文件分片(如果没有启用分片,整个文件被当成一个分片)后,上传之前执行。after-send-file
此hook在文件所有分片都上传完后,且服务端没有错误返回后执行。对于秒传来说,其实就是文件上传前,把内容读取出来,算出md5值,然后通过ajax与服务端验证进行验证, 然后根据结果选择继续上传还是掉过上传。
像这个操作里面有两个都是异步操作,文件内容blob读取和ajax请求。所以这个
handler
必须是异步的,怎样告诉组件此handler
是异步的呢?只需要在hanlder
里面返回一个promise对象就可以了,这样webuploader就会等待此过程,监听此promise的完成事件,自动继续。以下是此思路的简单实现。
关于断点续传
其实就是秒传分片,跟秒传整个文件是一个思路。关于md5验证这块,可以ajax请求验证,也可以在文件秒传验证的时候,把已经成功的分片md5列表拿到,这样分片验证的时候就只需要本地验证就行了,减少请求数。
具体实现和思路请查看这里https://github.com/fex-team/webuploader/issues/139