473,473 Members | 2,147 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

readfile() returning 0bytes

nathj
938 Recognized Expert Contributor
Hi,

It's been a while since I posted a question but now I'm really stuck.

The PHP below is what's causing my trouble. I need to be able to track when a member downloads a file, this forms part of the recommendation system.

The code does record the download, however, it doesn't actually download the file. The result is that a file of the correct name appears where it should but the size is 0kb. this is the same regardless of the file type.[php]
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/dataObject.php' ;
$loDB = new dataObject();
$lnLeaderID = $_GET['id'] ;
$lnFileID = $_GET['item'] ;
$lcFilePath = $_GET['extra4'] ;
// first check that the leader does not already have the download associated with them, if they do there is no reason to load it again.
$lcCheckSQL = "SELECT a.ID, a.frequency + 1 as newFrequency from leaderdownloadassociation a WHERE a.leaderID = $lnLeaderID AND a.downloadID = $lnFileID" ;
$laAssociation = $loDB->queryGetData(false, $lcCheckSQL) ;
if($laAssociation)
{
// update the frequency
$lnFrequency = $laAssociation[0]['newFrequency'] ;
$lcUpdate = "UPDATE leaderdownloadassociation set frequency = $lnFrequency, editdate = now() WHERE leaderID = $lnLeaderID AND downloadID = $lnFileID" ;
$loDB->iQuery($lcUpdate) ;
} else // first download
{
$lcTableName = "leaderdownloadassociation" ;
$lcFieldList = "leaderID, downloadID, frequency, createdate, editdate" ;
$lcValueList = "$lnLeaderID, $lnFileID, 1, now(), now()";
$loDB->queryInsert($lcTableName, $lcFieldList, $lcValueList, false) ;
}
// the calling file
$lcCallingFile = $_SERVER['HTTP_REFERER'] ;
$lcBaseCall = substr($lcCallingFile, 1, strpos($lcCallingfile, '?')-1) ;
// download the file
header("Content-Type: " . $_GET['extra2']);
header("Content-Length: " . $_GET['extra3']);
header('Content-Disposition: attachment; filename="' . $_GET['extra'] .'"');
if(file_exists($lcFilePath))
{
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($lcFilePath));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($lcFilePath));
ob_clean();
flush();
readfile($lcFilePath);
}
[/php]

The URL that calls this from the click of link may be something like:
[html]
<a title="John Kirkby" href="lib/recorddownload.php?item=2&id=1&extra=JohnKirkbyCAP pm.mp3
&extra2=audio/mpeg&extra3=5484191
&extra4=http://www.christianleadership.org.uk/download/audio/JohnKirkbyCAPpm.mp3"
class="inlineLink"
id="download2">John Kirkby</a>
[/html]

I've checked the permissions on the file and they are fine - 755, basically the file can be read by anyone, plus a bit more.

Can anyone spot the problem with this or suggest a better way of achieving the same result.

Cheers
nathj
Apr 14 '08 #1
5 5376
passion4code
4 New Member
One thing I've had issues with was the header("Content-Transfer-Encoding: binary\n");

I've sometimes found that with using php to force file downloads, a line return can cause mysterious issues to disappear.

This is a tricky area, because if the size is too big, you might not get the results you want with ob_clean() and flush().

Have you tried ob_end_flush()?
Apr 15 '08 #2
nathj
938 Recognized Expert Contributor
Unfortunately my mysterious issue hasn't disappeared.

I've tried your suggestions - both of them and still the file downloaded has 0 bytes. The file size on the server is around 5MB so it's not massive.

Do you have any other suggestions - even alternative approaches to this task. Basically I need to be able to allow a member to download a file and then record the fact that the file has been downloaded.

Would it work if I just issued a JS onclick thay called some PHP to update the database, is there a way to ensure that this then continues with the download as normal?


Thanks for your help.
nathj
Apr 15 '08 #3
passion4code
4 New Member

Would it work if I just issued a JS onclick thay called some PHP to update the database, is there a way to ensure that this then continues with the download as normal?


Thanks for your help.
nathj
What about doing an AJAX call to update your DB, then redirecting the browser to that specific file?
Apr 15 '08 #4
nathj
938 Recognized Expert Contributor
Hi,

right I am now getting sick of this module not working correctly so I've decided to start it again. The trouble is that I don't have a fresh pair of eyes or a fresh brain for it so I'm running out of ideas.

Here's what needs to happen.
1. the system prints a list of files for download based on eteries in the database (MySQL) - this bit is fine
2. the user can only see the list when they are logged in (aso working fine)
3. The user clicks on a download and:
a. the file is downloaded
b. the fact that they have downloaded is stored in the database - there is a table with the leaderID and downloadID. I simply want to update this.

I can get a or b to happen but not both. If b happens then the user is offered the php page to save. If a happens, most useful for the user, then the fact that it happened is not stored.

Approaches and results so far:

1. href and onclick
Using the .htaccess file I can ensure that files are downloaded and not opened so I used the href to list the file location and the onclick to call some JS which ran some php to update the database. the onclick returns true to ensure the default happens

The result here was that the file downloaded and the JS was called but it then errored with unknown status code

2. onclick only
Using just the onclick the JS function, via an AJAX methodology calls PHP to record the download and then outut the file

The result was that the event is recorded but the php page is downloaded!

3. href only
Using the href to call php page that logs the event in the DB and then users headers to download the file.

The result is that the download is recorded but the headers end up returning nothing, a file of 0bytes.

So that's what I have tried and I am stumped. I am totally flumoxed. If anyone has achieved this before or has an idea that would work please help me.

Cheers
nathj
Apr 17 '08 #5
nathj
938 Recognized Expert Contributor
Hi Again!

Well I thought it only polite to let you know that I have solved the problem.

After a decent nights sleep and a cup of coffee I started again and this tme I was thinking clearly. The solution was to use PHP to force the download. However, I have also updated the .htaccess file for reasons I will explain in a minute.

First the solution. I f a member is logged in and they click on a downloadable item the following code executes:
[html]
<a title=&quot;Church Attendance Survey&quot; href=&quot;lib/trackdownload.php?file=1&leader=1&quot; name=&quot;download1&quot; id=&quot;download1&quot; class=&quot;inlineLink&quot;>Church Attendance Survey</a>[/html]

The PHP looks like this:
[php]
<?php
$fileID = $_GET['file'];
$lnLeaderID = $_GET['leader'] ;
if($fileID>0)
{
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/dataObject.php' ;
$loDB = new dataObject();
$lcSelectFile=&quot;SELECT a.* FROM download a WHERE a.ID = $fileID&quot; ;
$laFile = $loDB->queryGetData(false, $lcSelectFile) ;
if($laFile)
{
foreach($laFile as $laRow)
{
$file_name = $laRow['URL'] ;
$file_name = '..' . substr($file_name, strpos($file_name, '/', 9)) ;
if(file_exists($file_name))
{
$lcCheckSQL = &quot;SELECT a.ID, a.frequency + 1 as newFrequency from leaderdownloadassociation a WHERE a.leaderID = $lnLeaderID AND a.downloadID = $fileID&quot; ;
$laAssociation = $loDB->queryGetData(false, $lcCheckSQL) ;
if($laAssociation)
{
$lnFrequency = $laAssociation[0]['newFrequency'] ;
$lcUpdate = &quot;UPDATE leaderdownloadassociation set frequency = $lnFrequency, editdate = now() WHERE leaderID = $lnLeaderID AND downloadID = $fileID&quot; ;
$loDB->iQuery($lcUpdate) ;
}
else
{
$lcTableName = &quot;leaderdownloadassociation&quot; ;
$lcFieldList = &quot;leaderID, downloadID, frequency, createdate, editdate&quot; ;
$lcValueList = &quot;$lnLeaderID, $fileID, 1, now(), now()&quot;;
$loDB->queryInsert($lcTableName, $lcFieldList, $lcValueList, false) ;
}
if(ini_get('zlib.output_compression'))
{
ini_set('zlib.output_compression', 'Off');
}
include(&quot;../config/mimetype.php&quot;) ;
$lcExtension = strtolower(substr(strrchr($file_name,'.'),1)) ;
if(array_key_exists($lcExtension, $laExtList))
{
$mime = $laExtList[$lcExtension] ;
}
else
{
$mime = 'application/force-download' ;
}
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private',false);
header('Content-Type: '.$mime);
header('Content-Disposition: attachment; filename=&quot;'.basename($file_name).'&quot;');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($file_name));
readfile($file_name);
exit();
}
}
}
}
?>
[/php]
Find out which file was requested, this is safer than passing the file path across in the QueryString. Then if the file exists, update the database to record the download, find out the mime type and using the headers force the download.

This works a treat for me now. so why the .htaccess file?
Expand|Select|Wrap|Line Numbers
  1. <FilesMatch &quot;\.(pdf|mp3|m3u|mp4|zip)$&quot;>
  2. ForceType application/octet-stream
  3. Header set Content-Disposition attachment
  4. </FilesMatch>
  5.  
Adding the above the .htaccess file ensures that when administrative users log in to and download something then the download is forced using a standard link to the file. I don't care what admin staff (me) download I'm only interested in members.

Well, that's detailed the lessons I learned and the solution I discovered. I must admit this post was useful, I tried a lot of the options mentioned and ldiscovered new things from each of them.

Finally, the main structure of the code I used above came from here. I changed the case to include and array (external file) and an array search.

Thanks again for all your help
Cheers
nathj
Apr 18 '08 #6

Sign in to post your reply or Sign up for a free account.

Similar topics

2
by: salsipius | last post by:
Hi all thanks in advance for reading. I am writing a serial port app with the help of many examples and I am not quite clear on how to get the data back from the method call? I have pasted the read...
0
by: Mustafa Ahmad Malik | last post by:
Hello, I have wrapped the Win32 pipes function in C#. I have created named pipe with PIPE_WAIT in pipemode parameter like this pipehandle = CreateNamedPipe(_pipeName, PIPE_ACCESS_DUPLEX,...
12
by: ORC | last post by:
Shouldn't 'ReadFile' block when timeouts are specified even when running in overlapped mode or am I wrong ??? Thanks Ole
3
by: Shawn August | last post by:
Hello: I am converting a working VB6 program to C#. During testing of the C# version, I noticed the ReadFile API is crashing. The parameters going into the this function are identical to the...
2
by: Schorschi | last post by:
Can't seemd to get ReadFile API to work! Returns invalid handle error? =========================================================================== Ok, the visual basic gurus, help! The...
1
by: Jørn Dahl-Stamnes | last post by:
I'm trying to replace <IMG SRC="some image"> with the usage of readfile, but without luck. I have seen examples like this: header ('Content-length: ' .filesize($image_file)); header...
4
by: Eric Renken | last post by:
I am trying to do an Overlapped ReadFile on a HID device and it just isn't working for me. The WaitForSingleObject keeps giving me an error "The system cannot find the file specified." This...
1
by: mkarja | last post by:
Hi, I have a windows MDI program that draws some shapes that can be saved into a file and read from that file. The save seems to work with the WriteFile function, but for some reason the...
15
by: Ketchup | last post by:
Hello everyone, I have been stuck with this problem for quite some time now. I am working in VB.NET, using framework 1.0. I have to keep the compatibility down to the original .NET framework...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
1
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
0
muto222
php
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.