Apache에서 gzip 압축을 사용할 때 컨텐츠 길이가 전송되지 않습니까?


13

이 아파치 동작을 이해하는 데 도움을 주시면 감사하겠습니다.

application / json의 iPhone Objective-C 앱에서 PHP와 통신하고 있습니다. Gzip 압축은 서버에서 활성화되고 클라이언트가 요청합니다.

내 .htaccess에서 :

AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php application/json

작은 요청의 경우 Apache는 'Content-Length'헤더를 설정합니다. 예를 들어 (이 값은 헤더에서 Objective-C로 출력됩니다)

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 185;     <-------------
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:27 GMT";
"Keep-Alive" = "timeout=3, max=149";
Server = Apache;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 217;

X-Uncompressed-Content-Length 는 압축되지 않은 JSON 문자열의 크기로 세트를 추가하는 헤더입니다.

보다시피,이 요청은 매우 작습니다 (217 바이트).

더 큰 요청의 헤더는 다음과 같습니다 (282888 바이트).

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:29 GMT";
"Keep-Alive" = "timeout=3, max=148";
Server = Apache;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 282888;

Content-Length는 제공되지 않습니다.

내 질문 :

  1. Apache가 더 큰 요청에 대해 Content-Length를 보내지 않는 이유는 무엇입니까?
  2. 'Contend-Encoding = gzip'이 설정되었다는 사실은 크기 차이를 확인할 수 없지만 gzip 압축이 더 큰 요청에서 여전히 작동하고 있음을 의미합니까?
  3. 더 큰 요청에 대해 Apache가 실제 Content-Length를 포함하여 사용자에게 데이터 사용량을보다 정확하게보고 할 수있는 방법이 있습니까?

이 응용 프로그램은 고가의 데이터 요금제에 사용할 수 있으므로 30-70 % 팽창 사용량이 아닌 실제 사용량을 사용자에게보고하고자합니다 (수백 개의 추가 KB가 거의 들리지 않을 수 있음) MB 당 10 달러!).

미리 감사드립니다.

답변:


14

Martin Fjordvalds 답변 추가 :

Apache는 압축 파일 크기가 DeflateBufferSize보다 큰 경우에만 청크 인코딩을 사용합니다. 따라서이 버퍼 크기를 늘리면 더 큰 파일에 대해서도 서버가 청크 인코딩을 사용하지 못하게되어 압축 된 데이터에 대해서도 Content-Length가 전송됩니다.

자세한 내용은 여기를 참조하십시오 : http://httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize


좋은데 이 문제를 해결하는 가장 빠른 방법 일 것입니다. 누구나 높은 수준의 사용자 정의가 필요한 경우 (예 : 다른 요청이 아닌 청크 청크) 수동 솔루션에 대한 내 대답 serverfault.com/a/183856/54957 을 참조하십시오 .
윌리엄 데니스

7

Apache가 청크 인코딩을 수행하는 것처럼 들리므로 전체 응답이 압축되기를 기다리지 않고 데이터가 압축되어 전송 될 수 있습니다. 그것은 꽤 표준적인 관행입니다. 아파치에 익숙하지 않은지 아는 데 익숙하지 않습니다.


정보 주셔서 감사합니다, 당신은 올바른 방향으로 나를 지적하고, 나는 그것을 해결했다.
윌리엄 데니스

받아 들였습니다. 그래도이 질문을 읽는 사람은 자세한 솔루션에 대한 내 대답을 읽으십시오. 기본적으로 응답을 수동으로 버퍼링하고 압축하여 청킹 (및 길이가 0이 아님)을 피할 수 있습니다.
윌리엄 데니스

수락 된 답변이 원래 질문에 대한 답변이 아니라 답변을 얻는 데 도움이 된 것임을 조금 혼란스럽게 생각합니다. 좀 더 명확하게하기 위해 아래에 게시 한 답변을 수락해야 할 수도 있습니다.
redbmk

@ redbmk 페어 포인트, 난 그냥 고맙게 보이고 싶지 않았다. 필립은 실제로 이것에 대한 완벽한 간단한 수정 사항을 가지고 있기 때문에 그의 이상을 받아 들였습니다.
윌리엄 데니스

5

좋아, 나는 이것을 해결했다. Martin F가 올바르게 지적했듯이 Apache는 응답을 청크하므로 내용 크기를 알 수 없습니다. 많은 사람들에게 이것이 바람직합니다 (페이지가 더 빨리로드됩니다). 다운로드 진행률을보고 할 수없는 비용이 발생합니다.

다운로드 진행 상황을보고하려는 나와 같은 사람들에게는 Apache 또는 PHP의 자동 gzip 지원을 사용하면 할 수있는 일이 거의 없습니다. 해결책은 수동으로 수행하는 것입니다. 소리보다 쉽습니다.

전체 파일을 전송하는 경우 다음은 PHP에서 하나의 청크 (Content-Length 포함)를 강제 실행하는 훌륭한 예입니다. http://www.php.net/manual/en/function.ob-start.php # 94741

생성 된 데이터를 전송하는 경우 위 샘플과 같이 gzencode를 사용하여 데이터를 인코딩하십시오. 전제 조건은 모든 출력 데이터가 변수에 저장되는 것입니다 (ob_start를 사용하여 버퍼링 한 다음 버퍼의 컨텐츠를 확보해야하는 경우이를 지원할 수 있음).

        // $replyBody is the entire contents of your reply

        header("Content-Type: application/json");  // or whatever yours is

        // checks if gzip is supported by client
        $pack = true;
        if(empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') === false)
        {
            $pack = false;
        }

        // if supported, gzips data
        if($pack) {
            header("Content-Encoding: gzip");
            $replyBody = gzencode($replyBody, 9, FORCE_GZIP);
        }

        // compressed or not, sets the Content-Length           
        header("Content-Length: " . mb_strlen($replyBody, 'latin1'));

        // outputs reply & exits
        echo $replyBody;
        exit;

그리고 짜잔!

직접 수행하는 또 다른 큰 이점은 압축 수준을 설정할 수 있다는 것입니다. 가장 높은 압축 수준으로 설정할 수 있으므로 (사용자가 데이터 비용을 덜 지불 할 수 있으므로) 모바일 응용 프로그램에 유용합니다. 반면 서버는 더 나은 CPU / 크기 균형을 위해 중간 압축 수준 만 사용합니다. 압축 수준은 httpd.conf (공유 호스팅에서는 불가능)를 편집 할 수있는 경우에만 변경할 수 있다고 생각합니다.

따라서 DEFLATE .htaccess 지시문을 유지했지만 응용 프로그램 / json 이외의 모든 내용은 위의 방식으로 인코딩됩니다.

다시 한 번 감사합니다. Martin F,이 문제를 해결하는 데 필요한 불꽃을 줬습니다. :)


1
또한 JSON 데이터 (키가 많이 반복됨)를 사용하면 한 번 에 77 % 나 크게 절약 할 수 있습니다. 즉 A의 큰 문제 ... MB 당 $을 1
윌리엄 Denniss

1
아마 strlen($replyBody)대신에 사용해야 합니다 mb_strlen($replyBody, 'latin1'). 내용 길이는 문자 수가 아닌 바이트 수이며 strlen ()이 제공하는 것입니다. latin1 문자는 항상 8 비트이므로 'latin1'과 함께 mb_strlen ()을 사용하면 latin1 문자가 아닌 바이트를 생성하는 인코딩에 문제가있을 수 있습니다.
orrd September
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.