44

これに似た質問がたくさんあるのを見たことがありますが、現在の問題を正確に説明している質問は見つかりませんでした。

クライアント側のコードが処理できるように、AJAXを介して大きな(0.5〜10 MBの)JSONドキュメントをロードするページがあります。ファイルが読み込まれると、予期しない問題は発生しません。ただし、ダウンロードに時間がかかるため、XHR Progress APIを利用して進行状況バーをレンダリングし、ドキュメントが読み込まれていることをユーザーに示してみました。これはうまくいきました。

次に、処理を高速化するために、gzipとdeflateを使用してサーバー側で出力を圧縮してみました。これも機能し、大幅な向上が見られましたが、プログレスバーが機能しなくなりました。

この問題をしばらく調べたところContent-Length、要求されたAJAXリソースとともに適切なヘッダーが送信されonProgressない場合、ダウンロードの進行状況がわからないため、イベントハンドラーが意図したとおりに機能しないことがわかりました。これが発生すると、呼び出されたプロパティがイベントオブジェクトにlengthComputable設定されます。false

これは理にかなっているので、出力の非圧縮長と圧縮長の両方でヘッダーを明示的に設定してみました。ヘッダーが送信されていることを確認でき、ブラウザーがコンテンツを解凍する方法を認識していることを確認できます。しかし、onProgressハンドラーはまだ報告しlengthComputable = falseます。

だから私の質問は:AJAX Progress APIでコンテンツをgzip圧縮/圧縮解除する方法はありますか?もしそうなら、私は今何を間違っていますか?


これは、リソースがChromeネットワークパネルに表示される方法であり、圧縮が機能していることを示しています。

ネットワークパネル

これらは関連するリクエストヘッダーであり、リクエストがAJAXであり、Accept-Encoding適切に設定されていることを示しています。

GET /dashboard/reports/ajax/load HTTP/1.1
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.99 Safari/537.22
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

これらは関連する応答ヘッダーであり、とが正しく設定されていることを示していContent-LengthますContent-Type

HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Encoding: deflate
Content-Type: application/json
Date: Tue, 26 Feb 2013 18:59:07 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
P3P: CP="CAO PSA OUR"
Pragma: no-cache
Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7
X-Powered-By: PHP/5.4.7
Content-Length: 223879
Connection: keep-alive

価値のあることとして、私はこれを標準(http)接続とセキュア(https)接続の両方で試しましたが、違いはありません。コンテンツはブラウザーで正常に読み込まれますが、ProgressAPIでは処理されません。


Adamの提案によると、サーバー側をgzipエンコーディングに切り替えてみましたが、成功も変更もありませんでした。関連する応答ヘッダーは次のとおりです。

HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Encoding: gzip
Content-Type: application/json
Date: Mon, 04 Mar 2013 22:33:19 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
P3P: CP="CAO PSA OUR"
Pragma: no-cache
Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7
X-Powered-By: PHP/5.4.7
Content-Length: 28250
Connection: keep-alive

繰り返しになりますが、コンテンツは適切にダウンロードおよびデコードされています。問題が発生しているのは、進行状況APIだけです。


Bertrandのリクエストによると、次のリクエストがあります。

$.ajax({
    url: '<url snipped>',
    data: {},
    success: onDone,
    dataType: 'json',
    cache: true,
    progress: onProgress || function(){}
});

そして、onProgressこれが私が使用しているイベントハンドラーです(それほどクレイジーではありません):

function(jqXHR, evt)
{
    // yes, I know this generates Infinity sometimes
    var pct = 100 * evt.position / evt.total;

    // just a method that updates some styles and javascript
    updateProgress(pct);
});
4

8 に答える 8

16

ソリューションのもう少し洗練されたバリエーションは、「x-decompressed-content-length」などのヘッダーをHTTP応答に設定し、コンテンツの完全に解凍された値をバイト単位で設定し、onProgressのxhrオブジェクトから読み取ることです。ハンドラ。

コードは次のようになります。

request.onProgress = function (e) {
  var contentLength;
  if (e.lengthComputable) {
    contentLength = e.total;
  } else {
    contentLength = parseInt(e.target.getResponseHeader('x-decompressed-content-length'), 10);
  }
  progressIndicator.update(e.loaded / contentLength);
};
于 2015-09-26T17:06:06.217 に答える
9

圧縮されたコンテンツ自体での使用の問題を解決することはできませんでしたがonProgress、この半単純な回避策を思いつきました。簡単HEADに言うと、リクエストと同時にサーバーにリクエストを送信し、GET十分な情報が得られたらプログレスバーを表示します。


function loader(onDone, onProgress, url, data)
{
    // onDone = event handler to run on successful download
    // onProgress = event handler to run during a download
    // url = url to load
    // data = extra parameters to be sent with the AJAX request
    var content_length = null;

    self.meta_xhr = $.ajax({
        url: url,
        data: data,
        dataType: 'json',
        type: 'HEAD',
        success: function(data, status, jqXHR)
        {
            content_length = jqXHR.getResponseHeader("X-Content-Length");
        }
    });

    self.xhr = $.ajax({
        url: url,
        data: data,
        success: onDone,
        dataType: 'json',
        progress: function(jqXHR, evt)
        {
            var pct = 0;
            if (evt.lengthComputable)
            {
                pct = 100 * evt.position / evt.total;
            }
            else if (self.content_length != null)
            {
                pct = 100 * evt.position / self.content_length;
            }

            onProgress(pct);
        }
    });
}

そしてそれを使用するには:

loader(function(response)
{
    console.log("Content loaded! do stuff now.");
},
function(pct)
{
    console.log("The content is " + pct + "% loaded.");
},
'<url here>', {});

サーバー側で、とリクエストX-Content-Lengthの両方にヘッダーを設定し(圧縮されていないコンテンツの長さを表す必要があります)、リクエストでのコンテンツの送信を中止します。GETHEADHEAD

PHPでは、ヘッダーの設定は次のようになります。

header("X-Content-Length: ".strlen($payload));

そして、それがHEADリクエストの場合は、コンテンツの送信を中止します。

if ($_SERVER['REQUEST_METHOD'] == "HEAD")
{
    exit;
}

実際の動作は次のとおりです。

スクリーンショット

下のスクリーンショットで時間がかかる理由はHEAD、サーバーがファイルを解析してファイルの長さを知る必要があるためですが、それは間違いなく改善でき、以前の場所からの改善です。

于 2013-03-08T20:44:59.980 に答える
4

ネイティブソリューションがないという理由だけで立ち往生しないでください。1行のハックで、Apache構成をいじることなく問題を解決できます(一部のホスティングでは禁止または非常に制限されています)。

救助するPHP:

var size = <?php echo filesize('file.json') ?>;

それだけです、あなたはおそらくすでに残りを知っていますが、ここでの参照としてそれは次のとおりです:

<script>
var progressBar = document.getElementById("p"),
    client = new XMLHttpRequest(),
    size = <?php echo filesize('file.json') ?>;

progressBar.max = size;

client.open("GET", "file.json")

function loadHandler () {
  var loaded = client.responseText.length;
  progressBar.value = loaded;
}

client.onprogress = loadHandler;

client.onloadend = function(pe) {
  loadHandler();
  console.log("Success, loaded: " + client.responseText.length + " of " + size)
}
client.send()
</script>

実例:

別のSOユーザーは、私がこのソリューションの有効性について嘘をついていると思っているので、ここで公開されています:http: //nyudvik.com/zip/、gzipで圧縮され、実際のファイルの重みは8 MB



関連リンク:

于 2013-03-06T12:08:00.433 に答える
2

サーバーのエンコーディングをgzipに変更してみてください。

リクエストヘッダーには3つの潜在的なエンコーディング(gzip、deflate、sdch)が表示されるため、サーバーはこれら3つのうちのいずれかを選択できます。応答ヘッダーから、サーバーがdeflateで応答することを選択していることがわかります。

Gzipは、追加のヘッダーとフッター(元の非圧縮長を含む)と異なるチェックサムアルゴリズムに加えて、deflateペイロードを含むエンコード形式です。

ウィキペディアでGzip

収縮にはいくつかの問題があります。不適切なデコードアルゴリズムを処理するレガシーの問題のため、deflateのクライアント実装は、処理している実装を特定するためだけに愚かなチェックを実行する必要がありますが、残念ながら、それでも間違っていることがよくあります。

Apacheが提供するテキストファイルにgzipの代わりにdeflateを使用するのはなぜですか?

あなたの質問の場合、ブラウザはおそらくデフレートファイルがパイプを下って来るのを見て、腕を上げてこう言います。進歩を正しくすることを心配する私、人間?」

サーバー構成を切り替えて応答がgzipで圧縮される場合(つまり、gzipがコンテンツエンコーディングとして表示される場合)、スクリプトが期待どおりに機能することを期待しています。

于 2013-03-02T08:23:07.823 に答える
2

進行状況を推定し、常にlengthComputabletrueに設定するライブラリを作成しました。

Chrome 64にはまだこの問題があります(バグを参照)

これは、この問題を修正するページに含めることができるjavascriptシムであり、標準new XMLHTTPRequest()を通常どおりに使用できます。

javascriptライブラリはここにあります:

https://github.com/AirConsole/xmlhttprequest-length-computable

于 2018-03-06T14:35:37.733 に答える
1

この解決策は私のために働いた。

deflateバッファーのサイズを増やして、一般的に圧縮される可能性のある最大のファイルサイズを約10 mbにカバーし、apache構成で9.3mbから3.2mbの圧縮を実現したため、代わりにcontent-lengthヘッダーが返されます。圧縮ファイルのロードがバッファサイズを超える場合に使用されるTransferEncoding仕様の結果として省略されています。詳細については、 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encodingを参照してください。圧縮で使用されるチャンク化されたエンコーディングヘッダー、およびhttps://httpd.apache.org/docs/2.4/mod/mod_deflate.html#deflatebuffersizeのdeflateバッファーサイズに関する詳細情報。

1-以下をapache構成に含めます。バッファー・サイズの値はバイト単位であることに注意してください。

<IfModule mod_deflate.c>
DeflateBufferSize 10000000
</IfModule>

2-Apacheサーバーを再起動します。

3- .htaccessファイルに以下を含めて、content-lengthヘッダーがJSHTTPリクエストに公開されていることを確認します。

<IfModule mod_headers.c>
    Header set Access-Control-Expose-Headers "Content-Length"
</IfModule>

4-進行状況の合計パーセンテージを計算する前のonDownloadProgressイベントで、合計バイト値を取得するために次を追加します。

var total = e.total;
if(!e.lengthComputable){
total = e.target.getResponseHeader('content-length') * 2.2;
} 

最初のステップは、複数のファイル間での圧縮の一般的な違いを調べ、2を掛けるかどうかを確認することです。たとえば、解凍されたファイルのサイズに最も近い値、つまり元のサイズになり、それに応じて乗算しますが、乗算によって結果がさらに小さいか等しいことを確認します。ただし、元のファイルサイズより大きくないため、ロードされたデータの到達が保証され、すべての場合で100をわずかに超える可能性があります。また、この問題の解決策には、進捗状況の計算を100に制限するというハッキーな機能強化があり、100%の実装に確実に到達するための関連ポイントを取りながら、進捗状況が超過したかどうかを確認する必要はありません。元のサイズとそれに応じて乗算しますが、乗算によって結果が元のファイルサイズよりも小さいか等しいが、大きくないことを確認します。したがって、ロードされたデータの場合、到達が保証され、すべての場合で100をわずかに超える可能性があります。また、この問題の解決策には、進捗状況の計算を100に制限するというハッキーな機能強化があり、100%の実装に確実に到達するための関連ポイントを取りながら、進捗状況が超過したかどうかを確認する必要はありません。元のサイズとそれに応じて乗算しますが、乗算によって結果が元のファイルサイズよりも小さいか等しいが、大きくないことを確認します。したがって、ロードされたデータの場合、到達が保証され、すべての場合で100をわずかに超える可能性があります。また、この問題の解決策には、進捗状況の計算を100に制限するというハッキーな機能強化があり、100%の実装に確実に到達するための関連ポイントを取りながら、進捗状況が超過したかどうかを確認する必要はありません。

私の状態では、これにより、各ファイル/リソースのロードがいつ完了したかを知ることができました。つまり、合計を次のようにチェックします。ここで、> =は、圧縮された合計乗算後に100%をわずかに超えて解凍された後、またはパーセンテージ計算方法を考慮に入れていました。が100に制限された後、代わりに==演算子を使用して、各ファイルのプリロードがいつ完了したかを確認します。また、ルートからこの問題を解決することを考えました。各ファイルの解凍されたロード済みの固定合計、つまり元のファイルサイズを保存し、それを使用して、進行状況のパーセンテージを計算するために、私の状態のリソースなどのファイルをプリロードします。これが私のonProgressイベント処理条件からのスニペットです。

// Some times 100 reached in the progress event more than once.
if(preloadedResources < resourcesLength && progressPercentage < 100) {
    canIncreaseCounter = true;
}
if(progressPercentage >= 100 && canIncreaseCounter && preloadedResources < resourcesLength) {
    preloadedResources++;
    canIncreaseCounter = false;
}

また、固定ソリューションとして予想されるロードされた合計使用量に注意してください。これは、プリロードまたはダウンロードするファイルに事前にアクセスできない場合を除いて、すべての状況で有効です。ほとんどの場合、プリロードするファイルがわかっているため、発生することはめったにないと思います。プリロードする前にそのサイズを取得できます。おそらく、HTTPの最初のリクエストでサーバーにある対象ファイルのサイズのPHPスクリプトリストを介して提供し、次にプリロードリクエストで、関連する元のファイルサイズを取得します。コードの一部として手動で保存する前に、プリロードされたリソースは連想配列で解凍されたサイズを固定し、ロードの進行状況を追跡するためにそれを使用できます。

読み込みの進行状況の実装のライブ例の追跡については、https: //zakaria.websiteの個人Webサイトで事前読み込みされているリソースを参照してください。

最後に、サーバーメモリへの余分な負荷を除いて、デフレートバッファサイズの増加による欠点を認識していません。この問題について誰かが入力した場合は、お知らせいただければ幸いです。

于 2020-11-21T14:09:09.947 に答える
0

私が考えることができる唯一の解決策は、データを手動で圧縮することです(サーバーとブラウザーにデータを残すのではなく)。これにより、通常のプログレスバーを使用でき、非圧縮バージョンよりもかなりの効果が得られます。たとえば、システムが最新世代のWebブラウザーでのみ動作する必要がある場合は、たとえばサーバー側(使用する言語に関係なく、zip関数またはライブラリがあると確信しています)でzipし、クライアント側で使用できます。zip.js。 _ より多くのブラウザサポートが必要な場合は、このSO回答を確認できます多数の圧縮および解凍機能(使用しているサーバー側言語でサポートされているものを選択するだけです)。全体として、これは実装がかなり簡単であるはずですが、ネイティブの圧縮/解凍よりもパフォーマンスが低下します(おそらくそれでも良好です)。(ところで、使用しているデータのタイプに適合し、データが十分に大きい場合に、理論的にはネイティブバージョンよりもパフォーマンスが向上する可能性があることを少し考えた後)

もう1つのオプションは、WebSocketを使用して、データをロードすると同時にすべてのパーツを解析/処理するパーツにデータをロードすることです(そのために、WebSocketは必要ありませんが、互いに数十回のhttpリクエストを実行するのは非常に面倒です。 )。これが可能かどうかは特定のシナリオによって異なりますが、レポートデータは部分的にロードできる種類のデータであり、最初に完全にダウンロードする必要はないように思えます。

于 2013-03-06T13:39:49.157 に答える
-2

私はこの問題を明確に理解していません。解凍はブラウザで行う必要があるため、発生しないはずです。

$ .ajaxはバイナリデータではうまく機能しないようであるため、jQueryから離れるか、jQueryをハックしてみてください。

参照:http://blog.vjeux.com/2011/javascript/jquery-binary-ajax.html

ajaxリクエストの独自の実装を試みることができます。https ://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest#Handling_binary_dataを参照してください。

javascriptでコンテンツのjsonを解凍してみることができます(コメントのリソースを参照してください)。

*アップデート2*

$ .ajax関数はprogressイベントハンドラーをサポートしていないか、jQueryドキュメントの一部ではありません(以下のコメントを参照)。

このハンドラーを機能させる方法は次のとおりですが、自分で試したことはありません: http ://www.dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5/

*アップデート3*

このソリューションでは、tierceサードパーティライブラリを使用して(?)jQuery ajax機能を拡張しているため、私の提案は適用されません

于 2013-03-08T00:11:57.487 に答える