基于AJAX/PHP的上传,带有进度条,适用于大型文件
我一直在尝试创建一个非flash上传面板,其中还显示一个进度条。 在我们的服务器上,我们有PHP5.3(目前无法升级到5.4,因此不能使用新的上传进度功能=>)。 我们不能使用基于flash的解决方案、扩展或类似产品 因此,我尝试将XMLHttpRequest与AJAX结合使用。 这里的问题是我只取得了部分成功 我已经设法上传并在服务器上保存了一个大约380MB的文件,但是,当尝试使用更大的文件(如4GB)时,它不会保存在服务器上(如果我在某一点上与Firebug进行检查,它会说“POST aborted”) 另一件奇怪的事情是,对于同一个文件,xhr.upload.loaded从xhr.upload.total的相同维度开始计算 有人知道如何解决这个问题,或者有其他的解决方案吗 客户端代码为:基于AJAX/PHP的上传,带有进度条,适用于大型文件,php,ajax,file,upload,progress,Php,Ajax,File,Upload,Progress,我一直在尝试创建一个非flash上传面板,其中还显示一个进度条。 在我们的服务器上,我们有PHP5.3(目前无法升级到5.4,因此不能使用新的上传进度功能=>)。 我们不能使用基于flash的解决方案、扩展或类似产品 因此,我尝试将XMLHttpRequest与AJAX结合使用。 这里的问题是我只取得了部分成功 我已经设法上传并在服务器上保存了一个大约380MB的文件,但是,当尝试使用更大的文件(如4GB)时,它不会保存在服务器上(如果我在某一点上与Firebug进行检查,它会说“POST ab
函数uploadToServer()
{
fileField=document.getElementById(“uploadedFile”);
var fileToUpload=fileField.files[0];
var xhr=new XMLHttpRequest();
var uploadStatus=xhr.upload;
uploadStatus.addEventListener(“进度”,函数(ev){
if(电动长度可计算){
$(“#上传百分比”).html((每辆装载/每辆总计)*100+“%”;
}
},假);
uploadStatus.addEventListener(“error”,函数(ev){$(“#error”).html(ev)},false);
uploadStatus.addEventListener(“加载”,函数(ev){$(“#错误”).html(“APPOSTO!”),false);
xhr.open(
“职位”,
“serverUpload.php”,
真的
);
setRequestHeader(“缓存控制”、“无缓存”);
setRequestHeader(“内容类型”、“多部分/表单数据”);
setRequestHeader(“X-File-Name”,fileToUpload.fileName);
setRequestHeader(“X-File-Size”,fileToUpload.fileSize);
setRequestHeader(“X-File-Type”,fileToUpload.Type);
//setRequestHeader(“内容类型”、“应用程序/八位字节流”);
xhr.send(fileToUpload);
}
$(函数(){
$(“#上载按钮”)。单击(上载到服务器);
});
HTML部分:
服务器端代码:
与web服务器相关的限制是PHP无法更改的。例如,在IIS中,它们的默认最大post请求大小为30MB…还有一个您可能遇到的最大超时。与大小无关,但您的post请求需要多长时间…即提交文件需要多长时间。这两种设置都可以受IIS或Apache的约束。我正在写一篇关于xhr.upload.loaded的奇怪行为的文章,它从一个大数字开始
我也有类似的问题,我找不到原因。
唯一可能有帮助的线索是,依靠ISP,问题有时会消失!
例如,当我在家测试时,它工作正常,我没有看到这种奇怪的行为,但在工作互联网上,问题仍然存在。您可以将代码与本教程进行比较。本教程可以上载任何大小的文件。它与您的代码非常相似。 file\u get\u contents()获取文件的内容,并使用内部指针将其放入RAM的缓冲区中。 如果您没有足够的ram,或者使用32位版本的apache/php,当试图分配太多内存时,它可能会崩溃 您可能想试试这样的方法:
$upload = fopen("php://input", "r");
while (!feof($upload)) {
file_put_contents($path . $filename, fread($upload, 4096), FILE_APPEND);
}
fclose($upload);
欢呼声其他人已经指出,在任何正确配置的生产PHP服务器上都会遇到一些限制。要启动的内存、post和文件最大值。此外,httpd服务通常也会限制这些功能 对于如此大的上传量,解决办法是将文件切成块,用不同的put或post(取决于浏览器)发送每个块 有一个已经存在的库能够上传区块文件,所以我将以它为例。为了支持分块上传,上传处理程序使用contentrange报头,该报头由插件为每个分块传输 UploadHandler类中的handle_file_upload函数是如何使用PHP在服务器端处理分块文件上载的一个很好的示例。- 该函数采用参数
$content\u range=null
,该参数在HTTP头中传递给服务器,并从$\u服务器['HTTP\u content\u range'检索代码>
稍后,我们需要确定是否将文件上传附加到一个已经存在的文件,因此我们设置了一个变量。如果HTTP请求报告的文件大小大于服务器上的实际文件大小,则$content\u range
变量不为空,并且文件存在,我们需要将此上载附加到现有文件
$append_file = $content_range && is_file($file_path) &&
$file->size > $this->get_file_size($file_path);
太好了!现在怎么办
所以现在我们需要知道我们是如何接收数据的。旧版本的Firefox不能使用multipart/formdata(POST)上传分块文件。对于客户端和服务器端,需要以不同的方式处理这些请求
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
// multipart/formdata uploads (POST method uploads)
if ($append_file) {
// append to the existing file
file_put_contents(
$file_path,
fopen($uploaded_file, 'r'),
FILE_APPEND
);
} else {
// this is a new chunked upload OR a completed single part upload,
// so move the file from the temp directory to the uploads directory.
move_uploaded_file($uploaded_file, $file_path);
}
}
else {
// Non-multipart uploads (PUT method support)
file_put_contents(
$file_path,
fopen('php://input', 'r'),
$append_file ? FILE_APPEND : 0
);
}
根据文档:分块文件上传仅受支持XHR文件上传和Blob API的浏览器支持,包括Google Chrome和Mozilla Firefox 4+-
要在Mozilla Firefox 4-6(Firefox 7之前支持XHR上传的Firefox版本)中进行分块上传,还必须将multipart选项设置为false。下面是在服务器端处理这些情况的代码
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
// multipart/formdata uploads (POST method uploads)
if ($append_file) {
// append to the existing file
file_put_contents(
$file_path,
fopen($uploaded_file, 'r'),
FILE_APPEND
);
} else {
// this is a new chunked upload OR a completed single part upload,
// so move the file from the temp directory to the uploads directory.
move_uploaded_file($uploaded_file, $file_path);
}
}
else {
// Non-multipart uploads (PUT method support)
file_put_contents(
$file_path,
fopen('php://input', 'r'),
$append_file ? FILE_APPEND : 0
);
}
最后,我们可以验证下载是否完成,或者放弃已取消的上载
$file_size = $this->get_file_size($file_path, $append_file);
if ($file_size === $file->size) {
$file->url = $this->get_download_url($file->name);
if ($this->is_valid_image_file($file_path)) {
$this->handle_image_file($file_path, $file);
}
} else {
$file->size = $file_size;
if (!$content_range && $this->options['discard_aborted_uploads']) {
unlink($file_path);
$file->error = $this->get_error_message('abort');
}
}
在客户端,您需要跟踪块。每篇文章发布后,我们将发送下一部分,直到没有更多的块剩余。示例库是jQuery的一个插件,它使jQuery变得非常简单。使用像您这样的裸XHR对象将需要更多的代码。它可能看起来像这样:
var chunksize = 1000000 // 1MB
var chunks = math.ceil(chunksize / fileToUpload.fileSize);
function uploadChunk(fileToUpload, chunk = 0) {
var xhr = new XMLHttpRequest();
var uploadStatus = xhr.upload;
uploadStatus.addEventListener("progress", function (ev) {
if (ev.lengthComputable) {
$("#uploadPercentage").html((ev.loaded / ev.total) * 100 + "%");
}
}, false);
uploadStatus.addEventListener("error", function (ev) {$("#error").html(ev)}, false);
uploadStatus.addEventListener("load", function (ev) {$("#error").html("APPOSTO!")}, false);
var start = chunksize*chunk;
var end = start+(chunksize-1)
if (end >= fileToUpload.fileSize) {
end = fileToUpload.fileSize-1;
}
xhr.open(
"POST",
"serverUpload.php",
true
);
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", fileToUpload.fileName);
xhr.setRequestHeader("X-File-Size", fileToUpload.fileSize);
xhr.setRequestHeader("X-File-Type", fileToUpload.type);
xhr.setRequestHeader("Content-Range", start+"-"+end+"/"+fileToUpload.fileSize);
xhr.send(fileToUpload);
}
for(c = 0; c < chunks; c++) {
uploadChunk(fileToUpload, c);
}
var chunksize=1000000//1MB
var chunks=math.ceil(ch
$file_size = $this->get_file_size($file_path, $append_file);
if ($file_size === $file->size) {
$file->url = $this->get_download_url($file->name);
if ($this->is_valid_image_file($file_path)) {
$this->handle_image_file($file_path, $file);
}
} else {
$file->size = $file_size;
if (!$content_range && $this->options['discard_aborted_uploads']) {
unlink($file_path);
$file->error = $this->get_error_message('abort');
}
}
var chunksize = 1000000 // 1MB
var chunks = math.ceil(chunksize / fileToUpload.fileSize);
function uploadChunk(fileToUpload, chunk = 0) {
var xhr = new XMLHttpRequest();
var uploadStatus = xhr.upload;
uploadStatus.addEventListener("progress", function (ev) {
if (ev.lengthComputable) {
$("#uploadPercentage").html((ev.loaded / ev.total) * 100 + "%");
}
}, false);
uploadStatus.addEventListener("error", function (ev) {$("#error").html(ev)}, false);
uploadStatus.addEventListener("load", function (ev) {$("#error").html("APPOSTO!")}, false);
var start = chunksize*chunk;
var end = start+(chunksize-1)
if (end >= fileToUpload.fileSize) {
end = fileToUpload.fileSize-1;
}
xhr.open(
"POST",
"serverUpload.php",
true
);
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", fileToUpload.fileName);
xhr.setRequestHeader("X-File-Size", fileToUpload.fileSize);
xhr.setRequestHeader("X-File-Type", fileToUpload.type);
xhr.setRequestHeader("Content-Range", start+"-"+end+"/"+fileToUpload.fileSize);
xhr.send(fileToUpload);
}
for(c = 0; c < chunks; c++) {
uploadChunk(fileToUpload, c);
}
<form enctype="multipart/form-data" method="post">
<input type="file" id="video_file" name="video_file" accept=".mp4, .avi, .mkv">
<input type="submit" class="btn btn-success" id="video-upload-btn" name="video_upload_btn" value="Upload">
<div class="video-bar">
<span class="video-bar-fill" id="video-bar-fill-id"><span class="video-bar-fill-text" id="video-bar-fill-text-id"></span></span>
</div>
</form>
.video-bar{
width: 100%;
background: #eee;
padding: 3px;
margin-bottom: 10px;
box-shadow: inset 0 1px 3px rgba(0,0,0,0.2);
border-radius: 3px;
box-sizing: border-box;
}
.video-bar-fill{
height: 20px;
display: block;
background: cornflowerblue;
width: 0;
border-radius: 3px;
transition: width 0.8s ease;
}
.video-bar-fill-text{
color: #fff;
padding: 3px;
}
<script type="text/javascript">
var app = app || {};
(function(video_op){
"use strict";
var video_ajax, video_getFormData, video_setProgress;
video_ajax = function(data){
var xmlhttp = new XMLHttpRequest(), uploaded;
xmlhttp.addEventListener('readystatechange', function(){
if(this.readyState==4){
if(this.status==200){
uploaded = JSON.parse(this.response);
console.log(uploaded);
if(typeof video_op.options.finished==='function'){
video_op.options.finished(uploaded);
}
} else {
if(typeof video_op.options.error === 'function'){
video_op.options.error();
}
}
}
});
xmlhttp.upload.addEventListener("progress", function(event){
var percent;
if(event.lengthComputable===true){
percent = Math.round((event.loaded / event.total) * 100);
video_setProgress(percent);
}
});
if(video_op.options.videoProgressBar!==undefined){
video_op.options.videoProgressBar.style.width=0;
}
if(video_op.options.videoProgressText!==undefined){
video_op.options.videoProgressText.innerText=0;
}
xmlhttp.open("post", video_op.options.videoProcessor);
xmlhttp.send(data);
};
video_getFormData = function(source1){
var data = new FormData(), i;
for(i=0;i<source1.length; i++){
data.append('video_file', source1[i]);
}
data.append("ajax", true);
return data;
};
video_setProgress = function(value){
if(video_op.options.videoProgressBar!==undefined){
video_op.options.videoProgressBar.style.width = value? value+"%":0;
}
if(video_op.options.videoProgressText!==undefined){
video_op.options.videoProgressText.innerText=value?value+"%":0;
}
};
video_op.videouploader = function(options){
video_op.options = options;
if(video_op.options.videoFiles !== undefined){
var videoFormDataValue = video_getFormData(video_op.options.videoFiles.files);
video_ajax(videoFormDataValue);
}
}
}(app));
document.getElementById("video-upload-btn").addEventListener("click", function(e){
e.preventDefault();
document.getElementById("video-upload-btn").setAttribute("disabled", "true");
var videof = document.getElementById('video_file'),
videopb = document.getElementById('video-bar-fill-id'),
videopt = document.getElementById('video-bar-fill-text-id');
app.videouploader({
videoFiles: videof,
videoProgressBar: videopb,
videoProgressText: videopt,
videoProcessor: "upload.php",
finished: function(data){
console.log(data);
},
error: function(){
console.log("error");
}
});
});
</script>
<?php
if(!empty($_FILES["video_file"]))
{
if(!empty($_FILES["video_file"]["error"]))
{
if(move_uploaded_file($_FILES["video_file"]["tmp_name"], __DIR__."/".$_FILES["video_file"]["name"] ))
{
echo "success";
}
else
{
echo "failed";
}
}
else
{
echo "error";
}
}
?>
Open php ini file -
sudo nano /etc/php5/apache2/php.ini
Update these values-
post_max_size = 6000M
upload_max_filesize = 6000M
restart apache
sudo /etc/init.d/apache2 restart