3

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

したがって、AJAXと組み合わせてXMLHttpRequestを使用してみました。ここでの問題は、私が部分的な成功しか達成していないことです。

なんとかサーバーに約380MBのファイルをアップロードして保存しましたが、4 GBのような大きなファイルを試してみると、サーバーに保存されません(Firebugで確認すると、 「POSTが中止されました」と言います)。

もう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')
);

?>
4

6 に答える 6

4

適切に構成された本番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)

$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(GoogleChromeとMozillaFirefox 4+を含む)をサポートするブラウザーでのみサポートされます-https : //github.com/blueimp/jQuery-File-Upload / wiki /Chunked-file-uploads

チャンクアップロードをMozillaFirefox4-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を超えるファイルを処理できないバグがありました。

于 2014-05-08T14:16:19.600 に答える
3

PHPで変更できないWebサーバーに関連する制限があります。たとえば、IISではデフォルトの最大POSTリクエストサイズは30MBです...また、最大タイムアウトが発生する可能性があります。サイズとは関係ありませんが、投稿リクエストにかかる時間...つまり、ファイルの送信にかかる時間です。どちらの設定も、IISまたはApacheによって制約できます。

于 2012-05-09T03:35:30.503 に答える
1

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

于 2012-11-07T10:26:55.863 に答える
0

多数で始まるxhr.upload.loadedの奇妙な動作について書いています...

私も同様の問題を抱えており、その理由を見つけることができませんでした。役立つかもしれない唯一の手がかりは、ISPによっては問題が消えることがあるということです!たとえば、自宅でテストすると正常に動作し、この奇妙な動作は見られませんが、職場のインターネットでは問題が残ります。

于 2012-05-19T13:09:53.293 に答える
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);

乾杯

于 2013-03-14T20:58:40.997 に答える
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;
}

アヤックス::

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

    }
?>

また、以下にリストされているphpini値を変更します。

  1. post_max_size
  2. upload_max_filesize

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
于 2015-10-23T10:26:19.277 に答える