我一直在尝试创建一个非Flash上传面板,它还会显示一个进度条。 在我们的服务器上,我们有PHP 5.3(目前无法升级到5.4,因此无法使用新的上传进度功能=> http://php.net/manual/en/session.upload-progress.php)。 我们不能使用基于闪存的解决方案,扩展或类似的。
因此我尝试将XMLHttpRequest与AJAX结合使用。 这里的问题是我只取得了部分成功。
我设法上传并在服务器上保存了大约380 MB的文件,但是,当尝试使用像4 GB这样的大文件时,它将不会保存在服务器上(如果我在一台服务器上查看Firebug)它会说“POST aborted”。)
另一个奇怪的事情是,使用相同的文件,xhr.upload.load以相同的xhr.upload.total维度开始,并从那里开始计数。
有谁知道如何解决这个问题或有替代解决方案?
客户端代码是:
<script type="application/javascript" src="jquery.js"></script>
<script type="application/javascript">
function uploadToServer()
{
fileField = document.getElementById("uploadedFile");
var fileToUpload = fileField.files[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);
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-Type", "application/octet-stream");
xhr.send(fileToUpload);
}
$(function(){
$("#uploadButton").click(uploadToServer);
});
</script>
HTML部分:
<form action="" name="uploadForm" method="post" enctype="multipart/form-data">
<input id="uploadedFile" name="fileField" type="file" multiple />
<input id="uploadButton" type="button" value="Upload!">
</form>
<div id="uploadPercentage"></div>
<div id="error"></div>
服务器端代码:
<?php
$path = "./";
$filename = $_SERVER['HTTP_X_FILE_NAME'];
$filesize = $_SERVER['CONTENT_LENGTH'];
$file = "log.txt";
$fo= fopen($file, "w");
fwrite($fo, $path . PHP_EOL);
fwrite($fo, $filename . PHP_EOL);
fwrite($fo, $filesize . PHP_EOL);
fwrite($fo, $path . $filename . PHP_EOL);
file_put_contents($path . $filename,
file_get_contents('php://input')
);
?>
答案 0 :(得分:3)
与Web无法通过PHP更改的Web服务器相关联。例如,它们是IIS中默认的最大发布请求大小30MB ...还有一个您可能遇到的最大超时。与大小无关,但是您的帖子请求需要多长时间...即,文件提交需要多长时间。这两种设置都可以由IIS或Apache约束。
答案 1 :(得分:3)
其他人已经指出,您将在正确配置的任何生产PHP服务器上遇到限制。内存,帖子和文件最大值开始。此外,httpd服务通常也会限制这些。
上传的答案如此之大,就是将文件剪切成块,将每个块发送到不同的put或post(取决于浏览器)。
已经存在一个能够上传块文件的库,所以我将以它为例。为了支持分块上传,上传处理程序使用Content-Range标头,该标头由插件为每个块传输。
UploadHandler类中的handle_file_upload函数是如何使用PHP处理服务器端的分块文件上传的一个很好的例子。 - https://github.com/blueimp/jQuery-File-Upload/blob/master/server/php/UploadHandler.php
function handle_file_upload($uploaded_file, $name, $size, $type, $error,
$index = null, $content_range = null)
该函数接受参数$content_range = null
,该参数在HTTP标头中传递给服务器,并从$_SERVER['HTTP_CONTENT_RANGE'];
稍后我们需要找出是否要将文件上传附加到已经存在的文件中,以便我们设置变量。如果HTTP请求中报告的文件大小大于服务器上的实际文件大小,则$content_range
变量不为NULL且文件存在,我们需要将此上载附加到现有文件。
$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);
}
}
根据文档:只有支持XHR文件上传的浏览器和Blob API支持分块文件上传,Blob API包括Google Chrome和Mozilla Firefox 4+ - https://github.com/blueimp/jQuery-File-Upload/wiki/Chunked-file-uploads
对于在Mozilla Firefox 4-6(Firefox 7之前支持XHR上载的Firefox版本)上工作的分块上传,多部分选项也必须设置为false。以下是在服务器端处理这些情况的代码。
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的插件,它使它变得非常简单。使用像您这样的裸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);
}
循环遍历块,依次上传每个块区域。请注意,Content-Range标头值的格式为 start-end / size 。范围从0开始,因此“end”只能是“size”的最大值1。您可以使用范围“start - ”来指示范围从“start”扩展到文件的末尾。
修改强>
只是认为这样可以在服务器上实现进度条,否则单个文件上传是不可能的。由于您知道每个块的大小以及每个请求的状态,因此每次运行循环时都可以相应地更新状态栏。
另外值得注意的是某些浏览器的限制。 Chrome和Firefox应该能够处理4GB文件,但低于9的IE版本有一个错误,无法处理大于2GB的文件。
答案 2 :(得分:1)
您可以将您的代码与本教程进行比较。本教程可以上传任何大小的文件。它与您的代码非常相似。 http://www.youtube.com/watch?v=pTfVK73CUk8
答案 3 :(得分:0)
我正在写关于xhr.upload.loaded的奇怪行为,它以大量数字开头......
我有类似的问题,我找不到原因。 可能有帮助的唯一线索是依赖于ISP,问题有时会消失! 例如,当我在家测试它工作正常,我没有看到这种奇怪的行为,但从工作互联网,问题仍然存在。
答案 4 :(得分:0)
file_get_contents()获取文件的内容,并使用内部指针将其放入RAM中的BUFFER中。 如果你没有足够的ram,或者有32位版本的apache / php,那么在尝试分配太多内存时它可能会崩溃。
你可能想尝试这样的事情:
$upload = fopen("php://input", "r");
while (!feof($upload)) {
file_put_contents($path . $filename, fread($upload, 4096), FILE_APPEND);
}
fclose($upload);
干杯
答案 5 :(得分:0)
我尝试使用ajax上传4GB的视频文件。这是成功的。 这是我的代码。
HTML ::
<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>
CSS ::
.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;
}
Ajax ::
<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>
SERVER SIDE ::
<?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";
}
}
?>
同时更改下面列出的php ini值。
如果您在linux / ubuntu中 - 请按照以下步骤操作
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