MyBB Community Forums

Full Version: Change how attachments are downloaded
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
At the moment MyBB is using file_get_contents() to let users download attachments.
The function is fine as long as the file to download is small or the forum is installed on a dedicated server.

I have a board running on shared server and when I try to download big files I get an error because PHP is unable to allocate, for example, 7MB.
The problem is file_get_contents() store the whole file in a string.

To solve the problem is enough to add a few lines to attachment.php and use fgets() instead of file_get_contents().

Search for echo file_get_contents($thumb); in attachment.php (there are two) and replace it with:

Code:
$handle = @fopen($settings['uploadspath']."/".$attachment['attachname'], "r");
    if ($handle) {
    �� while (!feof($handle)) {
    ������ $buffer = fgets($handle, 4096);
    ������ echo $buffer;
    �� }
    �� fclose($handle);
    }
THX A LOT!
This saved my day, since in my forum they pretty often share 10+MB files which couldnt be downloaded since PHP couldn allocate the memory (i assume) - anyhow the 2nd string is now:
echo file_get_contents($mybb->settings['uploadspath']."/".$attachment['attachname']);
so i guess the additional ,"r" is not needed anymore?

By the way: how can this post be from 2005 and is now in MyBB 1.4 Feedback? Toungue
It was just never deleted. But, how else are downloads supposed to be managed?

I mean it's not MyBB's fault you have your setting higher than the php settings on your server.
(2009-02-21, 03:59 AM)NetSage Wrote: [ -> ]I mean it's not MyBB's fault you have your setting higher than the php settings on your server.
No one said is mybb's fault Exclamation
I'm just wondering why this post is from 2005 and still valid... (and i'm happy that i found it)

I mean there must be reasons why it never got implemented? maybe its insecure or smth.? i dont have any php skills so i dont know...
Of course i could adopt my php memory settings but even for a 30mb file (which isnt really much anymore today) it needs 60MB Memory to read that file in to mem before someone can download it, thats really eating ressources.

Ahh i really didnt want to start a disscussion here, i just wanted to post my answer that i never said its mybb's fault Big Grin i Heart My
(2009-02-21, 02:12 PM)Nordmann Wrote: [ -> ]I'm just wondering why this post is from 2005 and still valid... (and i'm happy that i found it)
Why wouldn't it be valid?
(2009-02-21, 07:33 PM)Ryan Gordon Wrote: [ -> ]
(2009-02-21, 02:12 PM)Nordmann Wrote: [ -> ]I'm just wondering why this post is from 2005 and still valid... (and i'm happy that i found it)
Why wouldn't it be valid?
They're probably assuming that we've already changed how it's done.
(2009-02-21, 07:35 PM)Tom Loveric Wrote: [ -> ]They're probably assuming that we've already changed how it's done.
i dont assume that it should be already changed, i'm just wondering that this thread just exists since 2005 like a zombie and no one ever commented on that or why this method should not be used or why the read into memory method is used Big Grin
Well not being a developer, I don't know why file_get_contents() is used to get files and then read into a string. I know this method definitely seems better, though.
I recall somewhat suggesting this to Ryan once but I think we both forgot about it.
It will most likely be changed if attachment download resuming is added.


This is something I wrote ages ago (so is probably crap) but an idea at least:
// send 16KB chunks
define('OUTPUT_BUFFER', 16384);
function send_file($attachment)
{
	global $mybb;
	// resume?
	if(isset($_SERVER['HTTP_RANGE']))
	{
		$rangestr = substr($_SERVER['HTTP_RANGE'], strpos($_SERVER['HTTP_RANGE'], '=')+1);
		list($offset,$offsetEnd) = explode('-', $rangestr);
		$offset = intval($offset);
		$offsetEnd = intval($offsetEnd);
		
		if($offset > $offsetEnd && $offsetEnd)
		{
			$offsetEnd = 0;
		}
	}
	else
	{
		$offset = 0;
		$offsetEnd = 0;
	}
	
	if($offset > 0 || $offsetEnd > 0)
	{
		if($offsetEnd && $offsetEnd < $attachment['filesize'] - 1) $stopPt = $offsetEnd;
		else $stopPt = $attachment['filesize']-1;
		
		$newlen = $stopPt+1-$offset;
		header("HTTP/1.1 206 Partial Content");
		header("Content-Range: bytes=$offset-$stopPt/{$attachment['filesize']}");
		header("Content-Length: $newlen");
	}
	else
	{
		$newlen = $attachment['filesize']-1;
		header("Content-Range: bytes=0-$newlen/{$attachment['filesize']}");
		header("Content-Length: {$attachment['filesize']}");
	}
	
	$ext = get_extension($attachment['filename']);
	if($ext == "txt" || $ext == "htm" || $ext == "html" || $ext == "pdf")
	{
		header("Content-disposition: attachment; filename={$attachment['filename']}");
	}
	else
	{
		header("Content-disposition: inline; filename={$attachment['filename']}");
	}
	
	header("Accept-Range: bytes");
	header("Content-type: {$attachment['filetype']}");
	
	
	if(!($fp = fopen($mybb->settings['uploadspath']."/".$attachment['attachname'], 'rb')))
		error('Internal error occurred :(');
	if($offset > 0) fseek($fp, $offset);
	if($offsetEnd && $offsetEnd < $fsize-1)
	{
		$fpos = $offset;
		while($fpos <= $offsetEnd)
		{
			if($fpos + OUTPUT_BUFFER > $offsetEnd)
			{
				echo fread($fp, $offsetEnd - $fpos +1);
				break;
			}
			else
			{
				echo fread($fp, OUTPUT_BUFFER);
				$fpos += OUTPUT_BUFFER;
			}
		}
		// Note: the variable $fpos has no meaning after this - DO NOT REUSE IT!
	}
	else // if no end offset, use the more optimized? function
	{
		fpassthru($fp);
	}
	fclose($fp);
}
thanks
dear
Pages: 1 2