使用 html5 分块上传文件
我正在尝试使用 html5 的文件 API 分块上传文件,然后在服务器端用 php 重新组装它。我正在上传视频,但是当我在服务器端合并文件时,大小增加了,并且它变成了无效文件。请注意,以下 html5 代码仅适用于 chrome 浏览器。在 Chrome 9 中进行了测试,因为这使用了文件 API 的切片函数。 有人可以指导我吗? 感谢
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 源
<!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>
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
PHP source
<?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
<!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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我尝试解决这个问题,发现以下内容,希望对您有用:
1)您用于切片文件的 JS 函数已被弃用。我正在运行 Chrome v14,但控制台无法识别它。在我可以做任何事情之前,我必须将其更改为:
2)我尝试了更小的文件(大约10MB),但遇到了类似的问题 - 我的视频在上传后总是损坏。当我比较原始文件和“副本”时,我注意到一件奇怪的事情:文件的各个部分似乎混淆了 - 全部都在那里,但顺序错误。
我怀疑您当前的程序遇到的一个问题是没有采取措施确保文件按正确的顺序组装。我相信发生的情况是,您的 JS 多次运行 uploadFile,而不等待先前的上传完成,并且服务器尝试按照接收的顺序组装文件,但这与发送文件的顺序不同。
我能够通过以下方式证明这一点:让您的代码正常工作(进行一些修改,将其拼凑在一起,作为概念证明),在收到每个文件时为每个文件分配一个序列号,然后在收到所有部件后按顺序组装它们。完成此操作后,我可以在上传视频文件后播放它。
我认为你将不得不采取类似的措施。接收所有文件块,然后组装它们,或者至少确保您正在采取必要的措施按顺序组装它们。我不确定为什么你的文件会变大(我很早就观察到了这种现象),所以我怀疑这只是由于不同步文件块而产生的一些奇怪的副作用。
您立即遇到的一个困难是 Javascipt 中的 Blob 对象不支持更改或设置文件名,因此您无法在客户端以这种方式为文件提供唯一标识符。我所做的,作为一个简单的解决方法如下:
正如您可能在服务器端猜到的那样,我就使用该 GET 变量来分配标识符,并将其用作任何其他需要处理的基础在服务器上完成。
无论如何,这并不能直接解决文件大小增长的问题,所以我只能希望这会对您有所帮助;我很好奇你还发现了什么!
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:
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.
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.
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:
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.
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!
您好,我已经检查了您的 php 文件。我向其中添加了一些序列号。并更改了文件名属性并删除了 dubbel 文件创建。这里是。
对于 html 部分,我更改了多部分文件的上传方式。我将主题放入列表中,然后逐个上传。这是代码。
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.
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.
更新答案
在 Chrome ==>切片函数接受第二个参数作为长度。
在FF==> Slice 函数接受第二个参数作为结束。
代码示例
webkitslice 和 mozslice
已弃用,请使用本机"slice()"
代替。BlobBuilder
也已弃用使用Blob 构造函数
。资源:
http://updates.html5rocks.com/2012/06/Don-t-Build-斑点构造它们
https://developer.mozilla.org/en-US/docs/Web/API/Blob
将文件作为块读取并上传
Updated answer
In chrome ==> Slice Function accepts the second parameter as length.
In FF ==> Slice Function accepts the second parameter as end.
code samples
webkitslice and mozslice
is deprecated use native"slice()"
instead.BlobBuilder
also deprecated useBlob constructor
.Resources:
http://updates.html5rocks.com/2012/06/Don-t-Build-Blobs-Construct-Them
https://developer.mozilla.org/en-US/docs/Web/API/Blob
Reading files as chunks and uploading
Slice 函数接受第二个参数作为 length 。其中 mozSlice 接受第二个参数作为结束
Slice Function accepts the second parameter as length . Where as mozSlice accepts second parameter as end
根据 PHP 文档,fread 的长度以字节为单位,而不是位。您是否尝试使用 1000000 而不是 1048576 ?
According to the PHP documentation, fread takes the length in bytes, not bits. Did you try with 1000000 instead of 1048576 ?