我想要实现的是:
在前端,我使用html5文件api来读取文件,然后使用ajax将文件的内容上传到php后端,如果文件大小很小就可以了。但是,如果文件足够大,则会导致chrome崩溃。所以我使用file.slice将大文件拆分成块,当所有块都上传到php时,将块合并为一个完整的块。
代码如下:
前端:
<style>
#container {
min-width:300px;
min-height:200px;
border:3px dashed #000;
}
</style>
<div id='container'>
</div>
<script>
function addDNDListener(obj){
obj.addEventListener('dragover',function(e){
e.preventDefault();
e.stopPropagation();
},false);
obj.addEventListener('dragenter',function(e){
e.preventDefault();
e.stopPropagation();
},false);
obj.addEventListener('drop',function(e){
e.preventDefault();
e.stopPropagation();
var ul = document.createElement("ul");
var filelist = e.dataTransfer.files;
for(var i=0;i<filelist.length;i++){
var file = filelist[i];
var li = document.createElement('li');
li.innerHTML = '<label id="'+file.name+'">'+file.name+':</label> <progress value="0" max="100"></progress>';
ul.appendChild(li);
}
document.getElementById('container').appendChild(ul);
for(var i=0;i<filelist.length;i++){
var file = filelist[i];
uploadFile(file);
}
},false);
}
function uploadFile(file){
var loaded = 0;
var step = 1024*1024;
var total = file.size;
var start = 0;
var progress = document.getElementById(file.name).nextSibling;
var reader = new FileReader();
reader.onprogress = function(e){
loaded += e.loaded;
progress.value = (loaded/total) * 100;
};
reader.onload = function(e){
var xhr = new XMLHttpRequest();
var upload = xhr.upload;
upload.addEventListener('load',function(){
if(loaded <= total){
blob = file.slice(loaded,loaded+step+1);
reader.readAsBinaryString(blob);
}else{
loaded = total;
}
},false);
xhr.open("POST", "upload.php?fileName="+file.name+"&nocache="+new Date().getTime());
xhr.overrideMimeType("application/octet-stream");
xhr.sendAsBinary(e.target.result);
};
var blob = file.slice(start,start+step+1);
reader.readAsBinaryString(blob);
}
window.onload = function(){
addDNDListener(document.getElementById('container'));
if(!XMLHttpRequest.prototype.sendAsBinary){
XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
function byteValue(x) {
return x.charCodeAt(0) & 0xff;
}
var ords = Array.prototype.map.call(datastr, byteValue);
var ui8a = new Uint8Array(ords);
try{
this.send(ui8a);
}catch(e){
this.send(ui8a.buffer);
}
};
}
};
</script>
php代码:
<?php
$filename = "upload/".$_GET['fileName'];
//$filename = "upload/".$_GET['fileName']."_".$_GET['nocache'];
$xmlstr = $GLOBALS['HTTP_RAW_POST_DATA'];
if(empty($xmlstr)){
$xmlstr = file_get_contents('php://input');
}
$is_ok = false;
while(!$is_ok){
$file = fopen($filename,"ab");
if(flock($file,LOCK_EX)){
fwrite($file,$xmlstr);
flock($file,LOCK_UN);
fclose($file);
$is_ok = true;
}else{
fclose($file);
sleep(3);
}
}
问题是,在文件块全部上传到服务器并合并为新文件后,总文件大小小于原始文件大小,合并后的文件大小被破坏。问题在哪里以及如何解决?
答案 0 :(得分:4)
xhr.send(blob.slice(0,10))
如果您决定在将文件发送到服务器之前加密/解密/压缩文件,那么只有时间才能读取/切片文件。
但只有在有限的时间内,所有浏览器才开始支持流
那你应该看看fetch和ReadableStream
fetch(url, {method: 'post', body: new ReadableStream({...})})
如果您只需要将blob转发到服务器,只需执行以下操作:
xhr.send(blob_or_file)
并且浏览器将负责(正确地)读取它并且不消耗任何内存。然而,文件/ blob
答案 1 :(得分:3)
你的js脚本有一个小错误 我注意到 reader.onprogress 事件的触发次数比xhr load 事件多了很多次。在这种情况下,会跳过一些块。尝试在加载函数中增加加载的变量。
function uploadFile(file){
var loaded = 0;
var step = 1024*1024;
var total = file.size;
var start = 0;
var progress = document.getElementById(file.name).nextSibling.nextSibling;
var reader = new FileReader();
reader.onload = function(e){
var xhr = new XMLHttpRequest();
var upload = xhr.upload;
upload.addEventListener('load',function(){
loaded += step;
progress.value = (loaded/total) * 100;
if(loaded <= total){
blob = file.slice(loaded,loaded+step);
reader.readAsBinaryString(blob);
}else{
loaded = total;
}
},false);
xhr.open("POST", "upload.php?fileName="+file.name+"&nocache="+new Date().getTime());
xhr.overrideMimeType("application/octet-stream");
xhr.sendAsBinary(e.target.result);
};
var blob = file.slice(start,step);
reader.readAsBinaryString(blob); }
答案 2 :(得分:1)
此问题主要是由共享主机的全局限制引起的。如果限制被覆盖,它们通常会控制数据量并丢弃连接。 我尝试了几次和几种方法。始终坚持在同一个位置。目标文件较小且已损坏。即使上传一个较小的文件而不是这个大小,以便合并的数据符合限制,结果还可以。 你只有一次机会:增加最大尺寸,打开文件。 每次打开目标文件并将块内容写入其中,并且大小会覆盖限制,主机将断开连接。示例主持人:Strato 这里限制全局设置为16MB。我得到最大合并结果大小加倍这个大小。没有机会覆盖它。
答案 3 :(得分:-2)
你应该这个Javascript工作者线程。有关详细信息,请参阅此处
https://mytechbites.blogspot.in/2016/07/uploading-large-files-in-js.html