php 使用 html5 分块上传文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7853467/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
uploading a file in chunks using html5
提问by Vidya
I am trying to upload a file in chunks using html5's File API and then reassembling it on the server side in php. I am uploading a video but when i merge the files in server side the size has increased and it is becoming a invalid file . Please note the below html5 code only works in browser chrome . Tested in Chrome 9 as this uses file API's slice function . Can some one guide me on this ? Thanks
我正在尝试使用 html5 的文件 API 分块上传文件,然后在服务器端用 php 重新组装它。我正在上传视频,但是当我在服务器端合并文件时,大小增加了,并且变成了无效文件。请注意以下 html5 代码仅适用于浏览器 chrome 。在 Chrome 9 中测试,因为它使用文件 API 的切片功能。有人可以指导我吗?谢谢
PHP source
PHP源码
<?php
$target_path = "uploads/";
$tmp_name = $_FILES['fileToUpload']['tmp_name'];
$size = $_FILES['fileToUpload']['size'];
$name = $_FILES['fileToUpload']['name'];
$target_file = $target_path . basename($name);
$complete = "complete.mov";
$com = fopen("uploads/".$complete, "ab");
error_log($target_path);
// Open temp file
$out = fopen($target_file, "wb");
if ( $out ) {
// Read binary input stream and append it to temp file
$in = fopen($tmp_name, "rb");
if ( $in ) {
while ( $buff = fread( $in, 1048576 ) ) {
fwrite($out, $buff);
fwrite($com, $buff);
}
}
fclose($in);
fclose($out);
}
fclose($com);
?>
HTML Source
HTML 源代码
<!DOCTYPE html>
<html>
<head>
<title>Upload Files using XMLHttpRequest</title>
<script type="text/javascript">
window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
function sendRequest() {
var blob = document.getElementById('fileToUpload').files[0];
const BYTES_PER_CHUNK = 1048576; // 1MB chunk sizes.
const SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
while( start < SIZE ) {
var chunk = blob.slice(start, end);
uploadFile(chunk);
start = end;
end = start + BYTES_PER_CHUNK;
}
}
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}
function uploadFile(blobFile) {
//var file = document.getElementById('fileToUpload').files[0];
var fd = new FormData();
fd.append("fileToUpload", blobFile);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "upload.php");
xhr.onload = function(e) {
alert("loaded!");
};
xhr.send(fd);
//alert("oen over");
}
function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';
}
else {
document.getElementById('progressNumber').innerHTML = 'unable to compute';
}
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
xhr.abort();
xhr = null;
//alert("The upload has been canceled by the user or the browser dropped the connection.");
}
</script>
</head>
<body>
<form id="form1" enctype="multipart/form-data" method="post" action="upload.php">
<div class="row">
<label for="fileToUpload">Select a File to Upload</label><br />
<input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();"/>
<input type="button" value="cancel" onClick="uploadCanceled();"/>
</div>
<div id="fileName"></div>
<div id="fileSize"></div>
<div id="fileType"></div>
<div class="row">
<input type="button" onclick="sendRequest();" value="Upload" />
</div>
<div id="progressNumber"></div>
</form>
</body>
</html>
采纳答案by Vidya
Slice Function accepts the second parameter as length . Where as mozSlice accepts second parameter as end
Slice Function 接受第二个参数作为 length 。其中 mozSlice 接受第二个参数作为结束
回答by user470714
I tried to solve this problem and found the following things, which hopefully will be of use to you:
我尝试解决了这个问题,发现了以下几点,希望对你有用:
1) The JS function you are using for slicing the file is deprecated. I am running Chrome v14 and the console didn't recognize it. I had to change it to this before I could do anything:
1) 您用于切片文件的 JS 函数已弃用。我正在运行 Chrome v14,但控制台无法识别它。在我可以做任何事情之前,我不得不将其更改为:
var chunk = blob.webkitSlice(start, end);
2) I experimented with much smaller files (about 10MB), but had similar problems - my video was always corrupted after upload. When I compared the original and the 'copy' I noticed one thing peculiar: it seemed like the parts of the file were just mixed up - it was all there but in the wrong order.
2)我尝试了更小的文件(大约 10MB),但遇到了类似的问题 - 我的视频在上传后总是损坏。当我比较原件和“副本”时,我注意到一件事很奇怪:文件的各个部分似乎只是混在一起了——它们都在那里,但顺序错误。
I suspect one problem your current program suffers from is not taking measures to make sure that the files are assembled in the correct order. I believe what is happening is that your JS is running uploadFile several times, without waiting for the previous uploads to finish, and the server tries to assemble the files in the order it is received, but that is not the same order the files are sent.
我怀疑您当前的程序遇到的一个问题是没有采取措施确保文件以正确的顺序组装。我相信正在发生的事情是您的 JS 多次运行 uploadFile,没有等待之前的上传完成,并且服务器尝试按照收到的顺序组装文件,但这与发送文件的顺序不同.
I was able to prove this by getting your code to work (somewhat modified, hacked together just as a proof of concept), by assigning each file a sequence number as it was received, and then after all parts were received assembling them in order. After doing that, I was able to play my video file, after having uploaded it.
我能够通过让你的代码工作来证明这一点(有些修改,作为概念证明被黑客攻击),通过为每个文件分配一个序列号,然后在收到所有部件后按顺序组装它们。这样做之后,我可以在上传视频文件后播放它。
I think you are going to have to take a similar measure. Receive all the file chunks, and then assemble them, or at least make sure you're taking the necessary measures to assemble them in order. I'm not sure why your files would grow in size (I did observe this phenomenon, early on), so I suspect it is simply some bizarre side effect from otherwise not synchronizing the file chunks.
我认为你将不得不采取类似的措施。接收所有文件块,然后组合它们,或者至少确保您正在采取必要的措施来按顺序组合它们。我不确定为什么你的文件会变大(我很早就观察到了这种现象),所以我怀疑这只是一些奇怪的副作用,否则不同步文件块。
One difficulty you are immediately going to have is that the Blob object in Javasacript does not support changing or setting the file name, so you cannot on the client-side give the file a unique identifier that way. What I did, as a simple work around was the following:
您将立即遇到的一个困难是 Javasacript 中的 Blob 对象不支持更改或设置文件名,因此您不能在客户端以这种方式为文件提供唯一标识符。我所做的,作为一个简单的解决方法如下:
var i = 1;
while( start < SIZE ) {
var chunk = blob.webkitSlice(start, end);
uploadFile(chunk, i);
i++;
start = end;
end = start + BYTES_PER_CHUNK;
}
function uploadFile(blobFile, part) {
....
xhr.open("POST", "test.php?num=" + part);
....
}
As you can probably guess on the server side, I just then, use that GET variable to assign an identifier, and use that as a the basis for any other processing that needs to be done on the server.
正如您可能在服务器端猜到的那样,我刚刚使用该 GET 变量分配一个标识符,并将其用作需要在服务器上完成的任何其他处理的基础。
Anyways, this doesn't directly address the issue of the file size growing, so I can only hope this will help you; I'm curious to see what else you find out!
无论如何,这并不能直接解决文件大小增长的问题,所以我只能希望这对您有所帮助;我很想知道你还有什么发现!
回答by Jeroen
Hello I have checked your php file. I added some sequirity code to it. And changed the filename attribut and deleted the dubbel file creation. Here it is.
您好,我已经检查了您的 php 文件。我给它添加了一些 sequirity 代码。并更改了文件名属性并删除了dubbel文件创建。这里是。
<?php
session_start();
if ($_SESSION['newsession'] == false and $_SESSION['TypeUser'] == 'Admin' ){
$target_path = "../uploads/";
$tmp_name = $_FILES['fileToUpload']['tmp_name'];
$size = $_FILES['fileToUpload']['size'];
$name = $_FILES['fileToUpload']['name'];
$name2 = $_GET['filename'];
$target_file = $target_path.$name;
$complete =$target_path.$name2;
$com = fopen($complete, "ab");
error_log($target_path);
// Open temp file
//$out = fopen($target_file, "wb");
//if ( $out ) {
// Read binary input stream and append it to temp file
$in = fopen($tmp_name, "rb");
if ( $in ) {
while ( $buff = fread( $in, 1048576 ) ) {
// fwrite($out, $buff);
fwrite($com, $buff);
}
}
fclose($in);
//}
//fclose($out);
fclose($com);
}else{
echo'you are not logged in.';
}
?>
For the html part I changed the way the multipart files are uploaded. I put theme into a list and one by one I uploaded it. Here is the code.
对于 html 部分,我更改了上传多部分文件的方式。我把主题放到一个列表里,然后一个一个上传。这是代码。
<script type="text/javascript" >
function uploadchange() {
var input = document.getElementById("file");
var ul = document.getElementById("uploadlist");
while (ul.hasChildNodes()) {
ul.removeChild(ul.firstChild);
}
for (var i = 0; i < input.files.length; i++) {
var li = document.createElement("li");
thefilesize = input.files[i].fileSize||input.files[i].size;
if (thefilesize > 1024 * 1024){
thefilesize = (Math.round(thefilesize * 100 / (1024 * 1024)) / 100).toString() + 'MB';
}else{
thefilesize = (Math.round(thefilesize * 100 / 1024) / 100).toString() + 'KB';
}
li.innerHTML = input.files[i].name + " " + thefilesize ;
ul.appendChild(li);
}
if(!ul.hasChildNodes()) {
var li = document.createElement("li");
li.innerHTML = 'No Files Selected';
ul.appendChild(li);
}
sendRequest();
}
window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
function sendRequest() {
var blob = document.getElementById('file').files[0];
var BYTES_PER_CHUNK = 1048576; // 1MB chunk sizes.
var SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
window.uploadcounter=0;
window.uploadfilearray = [];
document.getElementById('progressNumber').innerHTML = "Upload: 0 % ";
while( start < SIZE ) {
var chunk = blob.slice(start, end);
window.uploadfilearray[window.uploadcounter]=chunk;
window.uploadcounter=window.uploadcounter+1;
start = end;
end = start + BYTES_PER_CHUNK;
}
window.uploadcounter=0;
uploadFile(window.uploadfilearray[window.uploadcounter],document.getElementById('file').files[0].name);
}
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}
function uploadFile(blobFile,filename) {
var fd = new FormData();
fd.append("fileToUpload", blobFile);
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "./system/upload2.php?filename="+filename);
xhr.onload = function(e) {
window.uploadcounter=window.uploadcounter+1;
if (window.uploadfilearray.length > window.uploadcounter ){
uploadFile(window.uploadfilearray[window.uploadcounter],document.getElementById('file').files[0].name);
var percentloaded2 = parseInt((window.uploadcounter/window.uploadfilearray.length)*100);
document.getElementById('progressNumber').innerHTML = 'Upload: '+percentloaded2+' % ';
}else{
document.getElementById('progressNumber').innerHTML = "File uploaded";
loadXMLDoc('./system/loaddir.php?url='+ window.currentuploaddir);
}
};
xhr.send(fd);
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
if (evt.target.responseText != ""){
alert(evt.target.responseText);
}
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
xhr.abort();
xhr = null;
//alert("The upload has been canceled by the user or the browser dropped the connection.");
}
</script>
<LINK HREF="./system/link.css" REL="stylesheet" TYPE="text/css">
</head>
<body>
<div id="fileselector">
<div id="containerback">
</div>
<div id="dirlijst">
</div>
<div id="container">
<h1>Upload file</h1>
<br />
<form name="form1" onSubmit="return uploadFile();" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype="multipart/form-data">
<div id="progressNumber"></div>
<input type="file" id="file" multiple name="uploads[]" style="visibility:hidden" onChange="uploadchange();">
<a href="#" onClick="document.getElementById('file').click();return false"><img src="system/iconfilemanager/upload.png" alt="upload file"></a>
<div id="uploadlist">
</div>
</form>
</div>
回答by user1834809
Updated answer
更新答案
In chrome ==> Slice Function accepts the second parameter as length.
在 chrome ==> Slice Function 中接受第二个参数作为长度。
In FF ==> Slice Function accepts the second parameter as end.
在 FF ==> Slice 函数接受第二个参数作为结束。
code samples
代码示例
fileorblob.slice(startingPosition, length) //for chrome
fileorblob.slice(startingPosition, end)//for FF
webkitslice and mozslice
is deprecated use native "slice()"
instead.
webkitslice and mozslice
不推荐使用 native"slice()"
代替。
BlobBuilder
also deprecated use Blob constructor
.
BlobBuilder
也不推荐使用Blob constructor
。
Resources:
资源:
http://updates.html5rocks.com/2012/06/Don-t-Build-Blobs-Construct-Them
http://updates.html5rocks.com/2012/06/Don-t-Build-Blobs-Construct-Them
https://developer.mozilla.org/en-US/docs/Web/API/Blob
https://developer.mozilla.org/en-US/docs/Web/API/Blob
回答by Pleblanc
According to the PHP documentation, fread takes the length in bytes, not bits. Did you try with 1000000 instead of 1048576 ?
根据 PHP 文档, fread 以字节为单位,而不是位。您是否尝试使用 1000000 而不是 1048576 ?