Jump to content


Photo

File Being Written To After Fclose?? What?


  • Please log in to reply
4 replies to this topic

#1 sammermpc

sammermpc
  • Members
  • PipPip
  • Member
  • 16 posts

Posted 28 June 2006 - 09:37 PM

I'm just trying to put together a simple download script to allow users to click on a hyperlink and download the corresponding file. Here's the code I have.
<?php
include('downloads.lib.php');
$fname = 'sent-mail';
$fpath = 'http://xxxxxxxx-blocked for privacy-xxxxxxxxxx/data/;

$bufsize = 4096;
if($fs = fopen ($fpath.$fname,"r")) {
  header("HTTP/1.1 200 OK");
  header("Content-Type: application/unknown");
  header("Content-Disposition:attachment; filename=$fname");
  header("Content-Transfer-Encoding: binary");
  while(!feof ($fs)) {
    $buf = fread ($fs, $bufsize);
    print($buf);
      flush();
  }
 fclose ($fs);
}


echo "TEST";
?>

This seems to work fine--the strange thing is, that last echo "TEST" is written to the end of the file. In fact, in other tests, everything within php tags seems to be written straight into the downloaded file. In this case, every file that is downloaded has a TEST appended to the end. Needless to say, this is not good.

Running PHP 5.0.4 on Apache 2.0. I'm not using file_exists, or filesize, because they don't seem to work with URLs, or at least, they either fail or return false, even if the file assuredly exists. Could there be a problem with the header() calls? I admittedly do not know a great deal about them.

ARGH!
Thanks,
Sam

#2 toplay

toplay
  • Staff Alumni
  • Advanced Member
  • 973 posts

Posted 29 June 2006 - 03:00 AM

You have to know the HTTP protocol (and the headers you're sending) in order to understand why it's doing that.

Just don't output anything other than the file after sending those headers. The closing of the file has nothing to do with the output that's being generated. Every output/echo/print is going to be downloaded. You're just closing the INPUT file and I assume you think that's closing the OUTPUT which is inaccurate.

FYI: It's easier to just use readfile() instead of the open/read/close. See:
[a href=\"http://us2.php.net/manual/en/function.readfile.php\" target=\"_blank\"]http://us2.php.net/manual/en/function.readfile.php[/a]

#3 sammermpc

sammermpc
  • Members
  • PipPip
  • Member
  • 16 posts

Posted 29 June 2006 - 07:45 PM

[!--quoteo(post=389078:date=Jun 28 2006, 10:00 PM:name=toplay)--][div class=\'quotetop\']QUOTE(toplay @ Jun 28 2006, 10:00 PM) View Post[/div][div class=\'quotemain\'][!--quotec--]
You have to know the HTTP protocol (and the headers you're sending) in order to understand why it's doing that.
[/quote]

So do you think that is the root of the problem? I'll admit I know virtually nothing of header protocols. I noticed that in many examples people use the header "Content-Length: $size", where $size is returned from filesize(). Unfortunately, that function does not seem to work for me (with urls?).

[!--quoteo(post=389078:date=Jun 28 2006, 10:00 PM:name=toplay)--][div class=\'quotetop\']QUOTE(toplay @ Jun 28 2006, 10:00 PM) View Post[/div][div class=\'quotemain\'][!--quotec--]
Just don't output anything other than the file after sending those headers.
[/quote]
It looks trivial to omit other output in the example I put, but this code is going to be embedded in a html representation of a large MySQL database that provides links to many remote files. I tried to plug it in, and though it happily downloads the files in question, it then proceeds to write the rest of the table to the file! This bloats little 10k calibration files to 300k behemoths.

I switched the implementation to readfile(), convenient because you don't have to use fopen, flcose. Thanks!

Anyhow, could the problem simply be in the header protocols? Anyone know the correct ones, or how I should approach this problem?


#4 toplay

toplay
  • Staff Alumni
  • Advanced Member
  • 973 posts

Posted 30 June 2006 - 07:14 PM

This forum was down late yesterday. phpfreak went with a new forum and design.

Anyway, as far as I know you can't display anything when you're forcing a download like that. An approach is to have a page (i.e. links.php) that contains all the available download file links. Each link points to the same download script passing a parameter to identify the file that should be downloaded. i.e. a href="http://www.example.c...php?fileid=123"

The download.php script would read the "fileid" value and determine which file it is and download it through the script example you show. The users experience would be a prompt from the browser asking them were they would like to save the file locally (on their client machine) and they would remain on the links.php page.

An alternative to the filesize() problem is to read the whole file into a string. Using strlen() you would then know how big the size of the file is. You would use that size in the Content-Length header and then echo the string (containing the file contents). Another way is to simply use output buffering. Example:

<?PHP
ob_start();
$fname = 'http://www.example.com/test.txt';

header('HTTP/1.1 200 OK');
header('Content-Type: application/unknown');
header('Content-Disposition: attachment; filename=' . basename($fname));
header('Content-Transfer-Encoding: binary');

readfile($fname);

header('Content-Length: ' . ob_get_length());

ob_end_flush();

echo 'This will not show in browser or be in file';
?>

The above code sample will not have the echo text be in the file. However, the echo won't be displayed in the browser either (as you seem to expect). The content length cuts it off before the text.

I've tried in the past using things like 'Content-Disposition: inline; filename=xxx' and it didn't work. Also content can be sent in chunks using 'Transfer-Encoding: chunked' header and specifying the size (in hex on a separate line) of each piece of data but that hasn't worked for me either. You're welcome to play around with different headers.

I recommend you use the links.php and download.php idea/approach.

FYI: Book recommendation:

HTTP Developer's Handbook (ISBN: 0672324547) by Chris Shiflett.

http://www.amazon.co...=glance&s=books

It does have a little PHP related example code, but it's mostly about the HTTP protocol itself.




#5 sammermpc

sammermpc
  • Members
  • PipPip
  • Member
  • 16 posts

Posted 30 June 2006 - 11:13 PM

Thanks toplay, the ob_end_close() solution is working for now. I originally did have it set up in a kind of link.php, downloads.php situation, though perhaps because I included the downloads.php file (to use a function written there), it didn't take care of my original issues.

I can see that if I linked to it instead it would.

Anyhow, it's working, and working well--so thanks!




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users