2012-04-20 67 views
3

プログレスバーも表示する非フラッシュアップロードパネルを作成しようとしています。 私たちのサーバーにはPHP 5.3があります(現在は5.4にアップグレードすることはできませんので、新しいアップロードの進行状況を使用することはできません>http://php.net/manual/en/session.upload-progress.php)。 Flashベースのソリューション、拡張機能などは使用できません。AJAX/PHPベースの大容量ファイル用プログレスバー付きアップロード

したがって、AJAXと組み合わせたXMLHttpRequestを使用しようとしました。 ここでの問題は、私が部分的に成功したことだけです。

サーバー上に約380 MBのファイルをアップロードして保存することができましたが、4 GBなどの大きなファイルを使用しようとするとサーバーに保存されません(Firebugで1ポイントは "POST aborted"と表示されます)。

もう1つの奇妙なことは、同じファイルでは、xhr.upload.loadedは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') 
); 

?> 
+1

ルック手順に従っ** ** max_post_size **と投稿サイズ/ファイルサイズに関する関連フラグ。 –

+0

私はすでにそれらをチェックして、すべてがうまく見えます。 – DiG

+0

それは冗談のように聞こえるが、時には愚かなことがあるので、私はとにかく尋ねる。「FAT32をファイルシステムとして使用していますか? –

答えて

3

ことができ、ウェブサーバに関連付けられた制限があります。 PHPによって変更されることはありません。例では、それらはIISで30MBのデフォルトの最大ポストリクエストサイズです...あなたが当てているかもしれない最大タイムアウトもあります。サイズとは関係ありませんが、あなたの投稿要求がどれくらいの時間を取っているか、つまりファイル提出の期間です。どちらの設定も、IISまたはApacheによって制約を受けることがあります。

0

私は同様の問題を抱えていると私は理由を見つけることができませんでした...多数で始まるxhr.upload.loadedの奇妙な行動について

を書いています。 ISPに応じて問題が消えることがあるという唯一の手掛かりです! たとえば、私は自宅でテストするとうまく動作しますが、私はこの奇妙な動作を見ませんが、仕事のインターネットからは、問題は残ります。

1

このチュートリアルでコードを比較できます。このチュートリアルでは、任意のサイズのファイルをアップロードできます。これはあなたのコードによく似ています。 http://www.youtube.com/watch?v=pTfVK73CUk8

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); 

乾杯

3

その他は、すでにあなたが適切に設定されている任意の産生PHPサーバー上に実行される限界があることを指摘しています。開始するメモリ、ポスト、およびファイルの最大値。加えて、httpdサービスは通常これらも同様に制限します。

アップロードの答えは、チャンクにファイルをカット(ブラウザにより異なります。)すでに存在するライブラリがあり

異なるプットまたはポストに各チャンクを送信することですので、大きな塊が可能ですファイルのアップロードがあるので、例として使用します。チャンクアップロードをサポートするために、アップロードハンドラは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) 

機能は、HTTPヘッダー内のサーバに渡され、私たちはへのファイルのアップロードを追加する場合はその後、我々は見つける必要があり$_SERVER['HTTP_CONTENT_RANGE'];

から取得された引数$content_range = nullを取りますすでに存在するファイルで、変数を設定します。 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をサポートするブラウザでサポートされている、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"の範囲にあることを示すことができます。

EDIT:

ただ、これはそれが可能それが単一ファイルのアップロードのためにそれ以外のことはできませんサーバー上のプログレスバーを実装することになるだろうと思いました。各チャンクのサイズと各リクエストのステータスを知っているので、ループを実行するたびにステータスバーを更新することができます。また、ノートの

は、特定のブラウザの制限です。 Chrome、Firefoxは4GBのファイルを処理できるはずですが、IEのバージョンが9より低いと、2GBを超えるファイルを処理できないバグがありました。

0

私は、Ajaxを使用して4ギガバイトのビデオファイルをアップロードしようとしました。それは成功だった。 ここに私のコードです。

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; 
} 

アヤックス::

<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"; 
     } 

    } 
?> 

以下のphpのini値も変更されています。

  1. post_max_sizeの
  2. upload_max_filesizeで

あなたは、Linux/Ubuntuのであるならば - これは** max_upload_file_size探し** php.iniの**オプションで

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 
関連する問題