[php] fpassthru 'vergeet' headers

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik wil een pdf file van een andere locatie doorsturen met fpasstru. Echter print hij dan gewoon de binaire output.

Hij print dus niet de headers. Hoe kan ik dit oplossen? Ik zat zelf te denken door eerst de headers op te halen, echter is er in php4 geen standaard functie om dat te doen (php5 wel, maar die draai ik niet).

Iemand tips?

Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

fpassthru() neemt gewoon een file en stuurt de inhoud daarvan door naar de browser. Headers moet je er zelf voorplakken met header().

All my posts are provided as-is. They come with NO WARRANTY at all.


Acties:
  • 0 Henk 'm!

  • DizzyWeb
  • Registratie: Februari 2001
  • Laatst online: 05-09 14:30

DizzyWeb

Ondertiteld

Zet er een header() boven.
PHP:
1
header("Content-type: application/pdf");

Als dat de juiste content-type is tenminste :)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Dat had ik al bedacht, het probleem is echter dat de header oa de lengte van de file bevat. Dus daarom moet ik de headers ofwel opnieuw maken (dan heb ik de lengte van de file in bytes van de file nodig), ofwel doorsturen.

Beide lukt me niet...

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
Zie de documentatie:

http://php.net/wrappers.http

De headers staan in $http_response_header, of zijn sinds PHP 4.3.0 met stream_get_meta_data() op te halen. Naast de "Content-Type" ook de "Content-Length".

edit:
niet echt relevant stukje...

[ Voor 72% gewijzigd door matthijsln op 16-08-2004 01:30 ]


Acties:
  • 0 Henk 'm!

  • Eegee
  • Registratie: Januari 2000
  • Laatst online: 20-09 23:09
Je zou het ook handmatig kunnen doen, zo ongeveer:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$remotesvr = 'pdfserver.com';
$remotepad = '/eenpdf.pdf';

$fd = fsockopen ($remotesvr, 80, $errno, $errstr, 10);
if ($fd!==FALSE)
{
  @socket_set_timeout($fd, 11);
  fputs ($fd, "GET ".$remotepad." HTTP/1.1\r\nHost: $remotesvr\r\n\r\n");
  $buffer = '';
  while (!feof ($fd)) {
    $buffer .= fread($fd, 4096);
  }            
  fclose ($fd);
  $body = strchr($buffer,"\r\n\r\n");
  $body = substr($body,4);
  $headers = substr($buffer,0,strlen($buffer)-strlen($body));    
}

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
Eegee schreef op 16 augustus 2004 @ 01:16:
Je zou het ook handmatig kunnen doen, zo ongeveer:
[code]
Errorchecking? Een buffer zo groot als de response (PDF files kunnen groot zijn)? :X

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
fsockopen had ik al gedaan, maar dat werkt niet als je een password en username wilt meegeven aan de url.

Ik kom nog niet helemaal eruit met die wrapper;

$header = stream_get_meta_data($fp);
print_r($header);

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Array
(
    [wrapper_data] => Array
        (
            [0] => HTTP/1.1 200 OK
            [1] => Date: Sun, 15 Aug 2004 23:31:21 GMT
            [2] => Server: Apache/2.0.45 (Unix) mod_ssl/2.0.45 OpenSSL/0.9.6c PHP/4.3.1
            [3] => Accept-Ranges: bytes
            [4] => X-Powered-By: PHP/4.3.1
            [5] => Content-length: 11986
            [6] => Content-Disposition: inline; filename=Bundel 2304394.pdf
            [7] => Connection: close
            [8] => Content-Type: application/pdf
        )

    [wrapper_type] => HTTP
    [stream_type] => socket
    [unread_bytes] => 7884
    [timed_out] => 
    [blocked] => 1
    [eof] => 
)


Nu dacht ik simpel te kunnen zeggen:
code:
1
echo $header[8];

enzo, maar dat doet ie niet 8)7

[ Voor 15% gewijzigd door Verwijderd op 16-08-2004 01:39 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Wat overigens ook een probleem is en hiermee hoop op te lossen. Het is niet altijd een pdf, maar soms gewoon html output. Bijvoorbeeld als de persoon geen toegang heeft tot de pdf...

Acties:
  • 0 Henk 'm!

  • bakakaizoku
  • Registratie: Januari 2002
  • Laatst online: 17-09 10:27
Verwijderd schreef op 16 augustus 2004 @ 01:39:
fsockopen had ik al gedaan, maar dat werkt niet als je een password en username wilt meegeven aan de url.

Ik kom nog niet helemaal eruit met die wrapper;

$header = stream_get_meta_data($fp);
print_r($header);

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Array
(
    [wrapper_data] => Array
        (
            [0] => HTTP/1.1 200 OK
            [1] => Date: Sun, 15 Aug 2004 23:31:21 GMT
            [2] => Server: Apache/2.0.45 (Unix) mod_ssl/2.0.45 OpenSSL/0.9.6c PHP/4.3.1
            [3] => Accept-Ranges: bytes
            [4] => X-Powered-By: PHP/4.3.1
            [5] => Content-length: 11986
            [6] => Content-Disposition: inline; filename=Bundel 2304394.pdf
            [7] => Connection: close
            [8] => Content-Type: application/pdf
        )

    [wrapper_type] => HTTP
    [stream_type] => socket
    [unread_bytes] => 7884
    [timed_out] => 
    [blocked] => 1
    [eof] => 
)


Nu dacht ik simpel te kunnen zeggen:
code:
1
echo $header[8];

enzo, maar dat doet ie niet 8)7
echo $header[wrapper_data][8];

?

PSN: bakakaizoku - WoW: Thiccblonde (GM of Phoenix Ascension) @ Twisting-Nether


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Okee dan, het werkt _/-\o_
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$fp = fopen($url, 'r');

/* Prior to PHP 4.3.0 use $http_response_header
   instead of stream_get_meta_data() */
foreach(stream_get_meta_data($fp) as $response) {

  /* Were we redirected? */
  if (substr(strtolower($response), 0, 10) == 'location: ') {
   /* update $url with where we were redirected to */
   $url = substr($response, 10);
  }

}

$header = stream_get_meta_data($fp);

header($header[wrapper_data][8]);
header($header[wrapper_data][6]);

$handle = fopen($url, "r");
fpassthru($handle);

Vast niet de meest efficiente of nette manier, maar 't werkt :)

Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

Verwijderd schreef op 16 augustus 2004 @ 01:48:
Okee dan, het werkt _/-\o_
code:
1
//snip

Vast niet de meest efficiente of nette manier, maar 't werkt :)
Probeer dit. :P

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$fp = fopen($url, 'r');

/* Prior to PHP 4.3.0 use $http_response_header
   instead of stream_get_meta_data() */
foreach(stream_get_meta_data($fp) as $response) {

  /* Were we redirected? */
  if (substr(strtolower($response), 0, 10) == 'location: ') {
   /* update $url with where we were redirected to */
   $url = substr($response, 10);
  }

}

$headers = stream_get_meta_data($fp);

for ($i = 0; $ < count($headers['wrapper_data']); $i++)
  header($headers['wrapper_data'][$i]);

$handle = fopen($url, "r");
fpassthru($handle);

Op die manier neem je consequent alle headers over, is toch wat netter. :)

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
Verwijderd schreef op 16 augustus 2004 @ 01:48:
Okee dan, het werkt _/-\o_
code:
1
2
header($header[wrapper_data][8]);
header($header[wrapper_data][6]);

Vast niet de meest efficiente of nette manier, maar 't werkt :)
Lijkt me niet dat die headers altijd op index 8 en 6 in die array staan; je zal echt even de array af moeten lopen en de headers eruit vissen.

Beetje jammer dat de fopen http wrapper de headers niet als key en value in een array heeft gezet. Let op dat header namen niet case-sensitive zijn.

Oh, wat verlang je soms toch naar een goed geimplementeerde object georienteerde HTTP client implementatie... (rant)

Dit stukje code had ik nog ergens om HTTP key en value te splitten:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Split HTTP header regels in key en value */

foreach($headers as $index => $header_line) {
    $kv = explode(':', $header_line, 2);
    /* Verwijder extra spatie na : */
    if($kv[1][0] === ' ') {
        $kv[1] = substr($kv[1], 1, strlen($kv[1])-1);
    }
    
    /* HTTP header keys zijn case-insensitive */
    $key_lc = strtolower($kv[0]);
    if($key_lc === "content-length" 
    || $key_lc === "content-type" 
    || $key_lc === "content-disposition") {
        /* Stuur de header door naar de client */
        ...
    }
}

[ Voor 13% gewijzigd door matthijsln op 16-08-2004 02:21 ]


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
Kan je eigenlijk niet gewoon een redirect sturen?

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
edit:
oeps, quote ipv edit

[ Voor 102% gewijzigd door matthijsln op 16-08-2004 02:16 ]


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

matthijsln schreef op 16 augustus 2004 @ 02:12:
Kan je eigenlijk niet gewoon een redirect sturen?
Zat ik me inderdaad ook al af te vragen. Wat is de overweging geweest om zo lastig te doen, terwijl je ook een location header kan setten. Heeft het nadelen voor jou om te redirecten?

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
NMe84 schreef op 16 augustus 2004 @ 02:21:
[...]

Zat ik me inderdaad ook al af te vragen. Wat is de overweging geweest om zo lastig te doen, terwijl je ook een location header kan setten. Heeft het nadelen voor jou om te redirecten?
Ik heb dit ook eens gehad en in dat geval zat de server in een lokaal netwerk en was alleen de PHP applicatie toegankelijk van "buiten". Een andere reden kan zijn dat er speciale cookies of username/password nodig zijn die de PHP applicatie moet toevoegen.

Acties:
  • 0 Henk 'm!

  • Grijze Vos
  • Registratie: December 2002
  • Laatst online: 28-02 22:17
matthijsln schreef op 16 augustus 2004 @ 01:32:
[...]


Errorchecking? Een buffer zo groot als de response (PDF files kunnen groot zijn)? :X
fpassthru doet intern ook gewoon bufferen hoor. Dus zelf bufferen van zo'n files geeft echt geen extra overhead.

Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
Grijze Vos schreef op 16 augustus 2004 @ 02:53:
[...]

fpassthru doet intern ook gewoon bufferen hoor. Dus zelf bufferen van zo'n files geeft echt geen extra overhead.
Deze zal toch echt geen buffer aanmaken zo groot als de hele stream. Hoogstens een buffer van aantal kb.

Edit:
Ik heb even in de source gekeken, er wordt een buffer gebruikt van 8kb (php-5.0.1\main\streams\streams.c:1143:_php_stream_passthru())

[ Voor 23% gewijzigd door matthijsln op 16-08-2004 03:38 ]


Acties:
  • 0 Henk 'm!

  • Sjaaky
  • Registratie: Oktober 2000
  • Laatst online: 20-09 23:02
matthijsln schreef op 16 augustus 2004 @ 03:32:
Deze zal toch echt geen buffer aanmaken zo groot als de hele stream. Hoogstens een buffer van aantal kb.

Edit:
Ik heb even in de source gekeken, er wordt een buffer gebruikt van 8kb (php-5.0.1\main\streams\streams.c:1143:_php_stream_passthru())
Dat zal dan vanaf versie 5 van php zijn. Volgens mij buffert fpassthru onder versie 4 de hele file.

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 20-09 00:06
Sjaaky schreef op 16 augustus 2004 @ 14:42:
[...]
Dat zal dan vanaf versie 5 van php zijn. Volgens mij buffert fpassthru onder versie 4 de hele file.
php-4.3.8\main\streams.c:1063:_php_stream_passthru()

8kb.

[ Voor 9% gewijzigd door matthijsln op 16-08-2004 17:43 ]


Acties:
  • 0 Henk 'm!

  • frickY
  • Registratie: Juli 2001
  • Laatst online: 18-09 14:42
Je kan met fsockopen ook prima je username en wachtwoord meegeven hor.
Username en wachtwoord aan elkaar plakken met een dubbele-punt er tussen, even base64_encoden, en meeschrijven in je socket;
PHP:
1
"Authorization: Basic ".base64_encode($user.":".$pass)."\r\n";

Acties:
  • 0 Henk 'm!

  • Sjaaky
  • Registratie: Oktober 2000
  • Laatst online: 20-09 23:02
Tja ik zie het 8kb.

Maar als ik een bestand van 700MB 'doorgeef'' met

PHP:
1
2
3
4
5
6
7
8
set_time_limit(0);
$name = 'testfile';
header("Content-type: application/octet-stream");
header("Content-Length: ".filesize($name));

$fp = fopen($name, 'rb');
fpassthru($fp);
fclose($fp);


Gaat het geheugengebruik van apache op mijn laptop (P3 1Ghz, 256MB) naar 220MB. Op mijn internetserver (P90, 16MB) gaat het geheugengebruik naar 20MB.

Doe ik het met onderstaande code (ipv van fpassthru) dan blijft het geheugen dat apache gebruik rond de 4MB.
PHP:
1
2
3
4
while(!feof($fp)) {
  $buf = fread($fp, 8096);
  print $buf;
}


Dit is met php 4.3.4 en apache 1.3.31. Geheugengebruik is gebaseerd op de kolom RES van het linux commando 'top' (VIRT en SHR gaan trouwens naar de 700m toe).

Dus hij buffert idd niet het hele bestand, maar echt efficient is allemaal niet. Tenzij de getallen die top geeft met een korrelt zout genomen moeten worden.

ps. beetje late reactie :O

[ Voor 6% gewijzigd door Sjaaky op 24-08-2004 20:13 ]

Pagina: 1