PDA

View Full Version : سوال: سرعت دانلود



n0o0b_sina
سه شنبه 08 اردیبهشت 1394, 22:05 عصر
سلام
دوستان چرا موقع ساخت لینک دانلود با php، وقتی HTTP/1.1 206 Partial Content رو به عنوان هدر میفرستیم حداکثر سرعته دانلودی که تعیین کردیم 10 برابر میشه؟! ولی وقتی هدر معمولی HTTP/1.1 200 OK میفرستیم همه چی درسته!!!
البته من یه تقلب کردم و موقع ارسال HTTP/1.1 206 Partial Content سرعت تعیین شده رو تقسیم 10 کردم تا درست بشه، ولی به نظرم این کار صحیح نیست.
ممنون میشم یکی راهنماییم کنه.

n0o0b_sina
سه شنبه 08 اردیبهشت 1394, 23:58 عصر
و یه مورد دیگ هم اینکه حداکثر سرعت دانلود رو مثلا برای یه فیلم 500 مگی میزنم 700 مگابایت توی HTTP/1.1 206 Partial Content دانلود منیجر خطا میده توی HTTP/1.1 200 OK هم بعد از 100 مگابایت.
چرا؟
اسکریپت هم میزارم خودتون بررسی کنید:

<?php
$speed = 10; // in kb/s
$resume = TRUE;
$file_path = 'movie.mkv';


// ==============================


// important - Report all errors except E_NOTICE
error_reporting(E_ALL &~ E_NOTICE);


// file does not exist
if ( ! is_file($file_path))
{
header("HTTP/1.0 404 Not Found");
exit;
}


$file_name = basename($file_path);
$file_size = filesize($file_path);
$file_type = mime_content_type($file_path);
$handle = fopen($file_path, 'rb');


// file couldn't be opened
if ( ! $handle)
{
header("HTTP/1.0 500 Internal Server Error");
exit;
}


header('Content-Disposition: attachment; filename='.$file_name);
header('Content-Type: '.$file_type);


// check if http_range is sent by browser (or download manager)
$seek_start = 0;
$seek_end = $file_size - 1;


if (isset($_SERVER['HTTP_RANGE']) AND $resume)
{
header('HTTP/1.1 206 Partial Content'); // this is important for resume option


list($size_unit, $ranges) = explode('=', $_SERVER['HTTP_RANGE'], 2);


if ($size_unit != 'bytes')
{
header('HTTP/1.1 416 Requested Range Not Satisfiable');
exit;
}


$range = current(explode(',', $ranges));
list($start, $end) = explode('-', $range, 2);


if ( ! empty($end) AND $end < $file_size)
$seek_end = abs(intval($end));
if ( ! empty($start) AND $seek_end >= $start)
$seek_start = abs(intval($start));


if ($speed !== NULL)
$speed /= 10;
}


header('Content-Length: '.($seek_end - $seek_start + 1));


flush();


set_time_limit(0);
fseek($handle, $seek_start);


while ( ! feof($handle) AND connection_status() == 0)
{
echo fread($handle, $speed * 1024);


flush();
sleep(1);
}


// file save was a success
fclose($handle);
exit;

n0o0b_sina
چهارشنبه 09 اردیبهشت 1394, 11:39 صبح
کسی نیست مرا یاری دهد؟

n0o0b_sina
چهارشنبه 09 اردیبهشت 1394, 22:25 عصر
بالا بالا بالا

n0o0b_sina
پنج شنبه 10 اردیبهشت 1394, 10:26 صبح
بالا بالا بالا :|

n0o0b_sina
پنج شنبه 10 اردیبهشت 1394, 20:05 عصر
ایول به خودم عجب سواله سختی :دی
از بینه 100 نفر یه نفر حتی جرات نکرده یه شکلک بزارهههههههههههه :قهقهه:

hamidhassas
پنج شنبه 10 اردیبهشت 1394, 23:02 عصر
:قهقهه::قهقهه:

amin1softco
پنج شنبه 10 اردیبهشت 1394, 23:47 عصر
10.2.7 206 Partial Content

The server has fulfilled the partial GET request for the resource. The request MUST have included a Range header field (section 14.35) indicating the desired range, and MAY have included an If-Range header field (section 14.27 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27)) to make the request conditional.
The response MUST include the following header fields:
- Either a Content-Range header field (section 14.16) indicating
the range included with this response, or a multipart/byteranges
Content-Type including Content-Range fields for each part. If a
Content-Length header field is present in the response, its
value MUST match the actual number of OCTETs transmitted in the
message-body.
- Date
- ETag and/or Content-Location, if the header would have been sent
in a 200 response to the same request
- Expires, Cache-Control, and/or Vary, if the field-value might
differ from that sent in any previous response for the same
variant
If the 206 response is the result of an If-Range request that used a strong cache validator (see section 13.3.3), the response SHOULD NOT include other entity-headers. If the response is the result of an If-Range request that used a weak validator, the response MUST NOT include other entity-headers; this prevents inconsistencies between cached entity-bodies and updated headers. Otherwise, the response MUST include all of the entity-headers that would have been returned with a 200 (OK) response to the same request.
A cache MUST NOT combine a 206 response with other previously cached content if the ETag or Last-Modified headers do not match exactly, see 13.5.4 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.4).
A cache that does not support the Range and Content-Range headers MUST NOT cache 206 (Partial) responses.



برگرفته از http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html


به جای sleep از usleep استفاده کنید و همچنین max_execution_time را باید مشخص کنید تا این مشکلی که می فرمایید حل بشه.
کلاس آماده : https://gist.github.com/psykzz/3687527

n0o0b_sina
جمعه 11 اردیبهشت 1394, 01:28 صبح
فکر کنم
set_time_limit(0);

max_execution_time رو هم تغییر میده به نامحدود چند تا جا قبلا در این مورد تحقیق کردم.
توی این کلاس 1000 میلی ثانیه رو تقسیم بر چهار کرده، چرا 4؟

n0o0b_sina
جمعه 11 اردیبهشت 1394, 01:32 صبح
منم تقسیم بر چهار کردم کلا ترکید، سرعته تعیین شده 100 کیلوبایت بر ثانیه، سرعت دانلود 8 مگابایت :|

n0o0b_sina
جمعه 11 اردیبهشت 1394, 01:34 صبح
وسط دانلود هم ارور میده اینجوری، معمولا استانداردش اینه 1 ثانیه توقف کنیم نه 1 ثانیه تقسیم بر چهار :(
راستی usleep هم فرقی با sleep نداره یکی بر اساس ثانیست یکی میلی ثانیه همین
---
حالا خوبه یه نفر همت کرد پست داد اینجا :دی

amin1softco
جمعه 11 اردیبهشت 1394, 07:35 صبح
فرقش اینه که وقتی شما مقدار ثانیه بزاری 1 ثانیه مقدار خیلی زیادیه برای صبر کردن و دانلود منیجر فکر می کنه سرور جوابی نمیده و دانلود را قطع می کنه ولی وقتی میلی ثانیه باشه احتمال قطع شدن کمتره !!


اونم عینا برات کپی می کنم


Some example:
Download first file(size = 1mb; download time 100 seconds )
After one second download second file ( size = 100 mb; dowload time = 10000 seconds)
First download set max_execution_time to 0
Second remeber _oldMaxExecTime as 0
First download end and return max_execution_time to old value
Second download end and return max_execution time to 0



و البته تنظیمات سرور هم مهمه این وسط چه آیا اجازه تغییر این مقادیر داده شده یا خیر
شما هم به جایی جوگیر شدن بهتر از کلاس آماده موجود استفاده کنید که با یک سرچ پیدا می شه برای مثال کلاس

https://github.com/perimeter/rate-limiter-php
یا
:throttler.php


<?php

/**
* QoS Bandwidth Throttler (part of Lotos Framework)
*
* Copyright (c) 2005-2010 Artur Graniszewski (aargoth@boo.pl)
* All rights reserved.
*
* @category Library
* @package Lotos
* @subpackage QoS
* @copyright Copyright (c) 2005-2010 Artur Graniszewski (aargoth@boo.pl)
* @license GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007
* @version $Id$
*/

/**
* Configuration interface.
*/
interface IThrottleConfig {}

/**
* Configuration class.
*/
class ThrottleConfig implements IThrottleConfig
{
/**
* Burst rate limit in bytes per second.
*
* @var int
*/
public $burstLimit = 80000;

/**
* Burst transfer rate time in seconds before reverting to the standard transfer rate.
*
* @var int
*/
public $burstTimeout = 20;

/**
* Standard rate limit in bytes per second.
*
* @var int
*/
public $rateLimit = 10000;

/**
* Enable/disable this module.
*
* @var bool
*/
public $enabled = false;
}

/**
* Another configuration class.
*/
class ThrottleConfigBySize implements IThrottleConfig
{
/**
* Maximal peak rate limit in bytes per second.
*
* @var int
*/
public $burstLimit = 80000;

/**
* Size (in bytes) of the already transferred data wjile in burst rate transfer before reverting to standard transfer rate.
*
* @var int
*/
public $burstSize = 2000;

/**
* Standard rate limit in bytes per second.
*
* @var int
*/
public $rateLimit = 10000;

/**
* Enable/disable this module.
*
* @var bool
*/
public $enabled = false;
}

/**
* The main class.
*/
class Throttle
{
/**
* Last heartbeat time in microseconds.
*
* @var int
*/
protected $lastHeartBeat = 0;

/**
* First (starting) heartbeat time in microseconds.
*
* @var int
*/
protected $firstHeartBeat = 0;

/**
* Number of bytes already sent.
*
* @var int
*/
protected $bytesSent = 0;

/**
* Total sending time in microseconds.
*
* @var int
*/
protected $sendingTime = 0;

/**
* Current transfer rate in bytes per second.
*
* @var int
*/
protected $currentLimit = 0;

/**
* Is this the last packet to send?
*
* @var bool
*/
protected $isFinishing = false;

/**
* @var ThrottleConfig
*/
protected $config;

/**
* Create new instance of throttler
*
* @param IThrottleConfig $config Configuration object or null to use system defaults
* @return Throttle
*/
public function __construct(IThrottleConfig $config = null) {
if(function_exists('apache_setenv')) {
// disable gzip HTTP compression so it would not alter the transfer rate
apache_setenv('no-gzip', '1');
}
// disable the script timeout if supported by the server
if(false === strpos(ini_get('disable_functions'), 'set_time_limit')) {
// suppress the warnings (in case of the safe_mode)
@set_time_limit(0);
}
if($config) {
$this->config = $config;
} else {
$this->config = new ThrottleConfig();
}

// set the burst rate by default as the current transfer rate
$this->currentLimit = $this->config->burstLimit;

// set the output callback
ob_start(array($this, 'onFlush'), $this->config->rateLimit);
}

/**
* Throttler destructor
*
* @return void
*/
public function __destruct() {
$this->isFinishing = true;
}

/**
* Throttling mechanism
*
* @param string $buffer Input buffer
* @return string Output buffer (the same as input)
*/
public function onFlush(& $buffer) {
// do nothing when buffer is empty (in case of implicit ob_flush() or script halt)
// and check if this is a last portion of the output, if it is - do not throttle
if($buffer === "" || $this->isFinishing) {
return $buffer;
}

// cache the buffer length for futher use
$bufferLength = strlen($buffer);

// cache current microtime to save us from executing too many system request
$now = microtime(true);

// initialize last heartbeat time if this is a first iteration of the callback
if($this->lastHeartBeat === 0) {
$this->lastHeartBeat = $this->firstHeartBeat = $now;
}

// calculate how much data have we have to send to the user, so we can set the appropriate time delay
// if the buffer is smaller than the current limit (per second) send it proportionally faster than the full
// data package
$usleep = $bufferLength / $this->currentLimit;
if($usleep > 0) {
usleep($usleep * 1000000);
$this->sendingTime += $usleep;
}

// check if the burst rate is active, and if we should switch it off (in both if cases)
if($this->currentLimit === $this->config->burstLimit && $this->config->burstLimit !== $this->config->rateLimit) {
if(isset($this->config->burstSize)) {
if($this->config->burstSize < $this->bytesSent + $bufferLength) {
$this->currentLimit = $this->config->rateLimit;
}
} else {
if($now > ($this->firstHeartBeat + $this->config->burstTimeout)) {
$this->currentLimit = $this->config->rateLimit;
}
}
}

// update system statistics
$this->bytesSent += $bufferLength;
$this->lastHeartBeat = $now;

return $buffer;
}

/**
* Returns throttle statistics.
*
* @return stdClass
*/
public function getStatistics() {
if(ob_get_level() > 0) {
ob_flush();
}
$stats = new stdClass();
$stats->bytesSent = $this->bytesSent;
$stats->sendingTime = $this->sendingTime;
$stats->averageRate = $this->sendingTime > 0 ? $this->bytesSent/$this->sendingTime : $stats->bytesSent;
return $stats;
}
}




و استفاده :

<?php

require("./throttler.php");

// create new config
$config = new ThrottleConfigBySize();
// enable burst rate for first 500000 bytes, after that revert to the standard transfer rate
$config->burstSize = 500000;
// set burst transfer rate to 50000 bytes/second
$config->burstLimit = 50000;
// set standard transfer rate to 15.000 bytes/second (after initial 30 seconds of burst rate)
$config->rateLimit = 15000;
// enable module (this is a default value)
$config->enabled = true;

// start throttling
$x = new Throttle($config);

header("Content-type: application/force-download");
header("Content-Disposition: attachment; filename=\"test.txt\"");
header("Content-Length: 60000000");

// generate 60.000.000 bytes file.
for($i = 0; $i < 60000000; $i++) {
echo "A";
}

n0o0b_sina
جمعه 11 اردیبهشت 1394, 08:16 صبح
عزیزم من همه ی اینارو تست کردم، نزدیک به 10 تا کده آماده دیدم و اینی که نوشتم از روی اوناست توی دانلود منیجر دقت کردید چی مینویسه؟ نوشته kb/s? ننوشته kb/s/4
کد رو بالا گذاشتم خدمتتون مقدار ثانیه رو از 1 ثانیه تغییر بدید ببینید چه اتفاقی میفته (با idm تست کنید)، هیچ کدوم از کدهای شما قابلیت resume ندارن، من مشکلی توی حالت عادی ندارم وقتی قابلیت resume توی کد بالا true میشه سرعت 10 برابر میشه و وقتی مقدار حداکثره سرعت رو توی حالت resume بالاتر از 700 میزاریم ارور میده توی حالت عادی هم بالاتر از 100.
درضمن قرار نیست یه کوچولو شوخی به کسی بر بخوره :) بازم ممنون از شما

amin1softco
جمعه 11 اردیبهشت 1394, 10:39 صبح
من هیچ کدوم اینا رو تست نکردم ولی این کلاس به نظرم باید مشکل resume شمارو حل کنه

<?

/**
@author Nguyen Quoc Bao <quocbao.coder@gmail.com>
@version 1.3
@desc A simple object for processing download operation , support section downloading
Please send me an email if you find some bug or it doesn't work with download manager.
I've tested it with
- Reget
- FDM
- FlashGet
- GetRight
- DAP

@copyright It's free as long as you keep this header .
@example

1: File Download
$object = new downloader;
$object->set_byfile($filename); //Download from a file
$object->use_resume = true; //Enable Resume Mode
$object->download(); //Download File

2: Data Download
$object = new downloader;
$object->set_bydata($data); //Download from php data
$object->use_resume = true; //Enable Resume Mode
$object->set_filename($filename); //Set download name
$object->set_mime($mime); //File MIME (Default: application/otect-stream)
$object->download(); //Download File

3: Manual Download
$object = new downloader;
$object->set_filename($filename);
$object->download_ex($size);
//output your data here , remember to use $this->seek_start and $this->seek_end value :)

**/

class httpdownload {

var $data = null;
var $data_len = 0;
var $data_mod = 0;
var $data_type = 0;
var $data_section = 0; //section download
/**
* @var ObjectHandler
**/
var $handler = array('auth' => null);
var $use_resume = true;
var $use_autoexit = false;
var $use_auth = false;
var $filename = null;
var $mime = null;
var $bufsize = 2048;
var $seek_start = 0;
var $seek_end = -1;

/**
* Total bandwidth has been used for this download
* @var int
*/
var $bandwidth = 0;
/**
* Speed limit
* @var float
*/
var $speed = 0;

/*-------------------
| Download Function |
-------------------*/
/**
* Check authentication and get seek position
* @return bool
**/
function initialize() {
global $HTTP_SERVER_VARS;

if ($this->use_auth) //use authentication
{
if (!$this->_auth()) //no authentication
{
header('WWW-Authenticate: Basic realm="Please enter your username and password"');
header('HTTP/1.0 401 Unauthorized');
header('status: 401 Unauthorized');
if ($this->use_autoexit) exit();
return false;
}
}
if ($this->mime == null) $this->mime = "application/octet-stream"; //default mime

if (isset($_SERVER['HTTP_RANGE']) || isset($HTTP_SERVER_VARS['HTTP_RANGE']))
{

if (isset($HTTP_SERVER_VARS['HTTP_RANGE'])) $seek_range = substr($HTTP_SERVER_VARS['HTTP_RANGE'] , strlen('bytes='));
else $seek_range = substr($_SERVER['HTTP_RANGE'] , strlen('bytes='));

$range = explode('-',$seek_range);

if ($range[0] > 0)
{
$this->seek_start = intval($range[0]);
}

if ($range[1] > 0) $this->seek_end = intval($range[1]);
else $this->seek_end = -1;

if (!$this->use_resume)
{
$this->seek_start = 0;

//header("HTTP/1.0 404 Bad Request");
//header("Status: 400 Bad Request");

//exit;

//return false;
}
else
{
$this->data_section = 1;
}

}
else
{
$this->seek_start = 0;
$this->seek_end = -1;
}

return true;
}
/**
* Send download information header
**/
function header($size,$seek_start=null,$seek_end=null) {
header('Content-type: ' . $this->mime);
header('Content-Disposition: attachment; filename="' . $this->filename . '"');
header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T' , $this->data_mod));

if ($this->data_section && $this->use_resume)
{
header("HTTP/1.0 206 Partial Content");
header("Status: 206 Partial Content");
header('Accept-Ranges: bytes');
header("Content-Range: bytes $seek_start-$seek_end/$size");
header("Content-Length: " . ($seek_end - $seek_start + 1));
}
else
{
header("Content-Length: $size");
}
}

function download_ex($size)
{
if (!$this->initialize()) return false;
ignore_user_abort(true);
//Use seek end here
if ($this->seek_start > ($size - 1)) $this->seek_start = 0;
if ($this->seek_end <= 0) $this->seek_end = $size - 1;
$this->header($size,$seek,$this->seek_end);
$this->data_mod = time();
return true;
}

/**
* Start download
* @return bool
**/
function download() {
if (!$this->initialize()) return false;

$seek = $this->seek_start;
$speed = $this->speed;
$bufsize = $this->bufsize;
$packet = 1;

//do some clean up
@ob_end_clean();
$old_status = ignore_user_abort(true);
@set_time_limit(0);
$this->bandwidth = 0;

$size = $this->data_len;

if ($this->data_type == 0) //download from a file
{

$size = filesize($this->data);
if ($seek > ($size - 1)) $seek = 0;
if ($this->filename == null) $this->filename = basename($this->data);

$res = fopen($this->data,'rb');
if ($seek) fseek($res , $seek);
if ($this->seek_end < $seek) $this->seek_end = $size - 1;

$this->header($size,$seek,$this->seek_end); //always use the last seek
$size = $this->seek_end - $seek + 1;

while (!(connection_aborted() || connection_status() == 1) && $size > 0)
{
if ($size < $bufsize)
{
echo fread($res , $size);
$this->bandwidth += $size;
}
else
{
echo fread($res , $bufsize);
$this->bandwidth += $bufsize;
}

$size -= $bufsize;
flush();

if ($speed > 0 && ($this->bandwidth > $speed*$packet*1024))
{
sleep(1);
$packet++;
}
}
fclose($res);

}

elseif ($this->data_type == 1) //download from a string
{
if ($seek > ($size - 1)) $seek = 0;
if ($this->seek_end < $seek) $this->seek_end = $this->data_len - 1;
$this->data = substr($this->data , $seek , $this->seek_end - $seek + 1);
if ($this->filename == null) $this->filename = time();
$size = strlen($this->data);
$this->header($this->data_len,$seek,$this->seek_end);
while (!connection_aborted() && $size > 0) {
if ($size < $bufsize)
{
$this->bandwidth += $size;
}
else
{
$this->bandwidth += $bufsize;
}

echo substr($this->data , 0 , $bufsize);
$this->data = substr($this->data , $bufsize);

$size -= $bufsize;
flush();

if ($speed > 0 && ($this->bandwidth > $speed*$packet*1024))
{
sleep(1);
$packet++;
}
}
} else if ($this->data_type == 2) {
//just send a redirect header
header('location: ' . $this->data);
}

if ($this->use_autoexit) exit();

//restore old status
ignore_user_abort($old_status);
set_time_limit(ini_get("max_execution_time"));

return true;
}

function set_byfile($dir) {
if (is_readable($dir) && is_file($dir)) {
$this->data_len = 0;
$this->data = $dir;
$this->data_type = 0;
$this->data_mod = filemtime($dir);
return true;
} else return false;
}

function set_bydata($data) {
if ($data == '') return false;
$this->data = $data;
$this->data_len = strlen($data);
$this->data_type = 1;
$this->data_mod = time();
return true;
}

function set_byurl($data) {
$this->data = $data;
$this->data_len = 0;
$this->data_type = 2;
return true;
}

function set_lastmodtime($time) {
$time = intval($time);
if ($time <= 0) $time = time();
$this->data_mod = $time;
}

/**
* Check authentication
* @return bool
**/
function _auth() {
if (!isset($_SERVER['PHP_AUTH_USER'])) return false;
if (isset($this->handler['auth']) && function_exists($this->handler['auth']))
{
return $this->handler['auth']('auth' , $_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
}
else return true; //you must use a handler
}

}

?>

تستش :


<?

$FILENAME = ""; //SET YOUR FILE HERE

if (!$FILENAME || !file_exists($FILENAME)) {
echo "Please set your target file \$FILENAME on line 2\n";
exit();
}

include_once "class.httpdownload.php";

$object = new httpdownload;

$bandwidth = @intval(implode('',file('bandwidth.txt'))) / 1024;
if ($bandwidth > 1024)
{
$bandwidth = round($bandwidth / 1024 , 2);
$bandwidth .= " MB";
}
else
{
$bandwidth .= " KB";
}

switch (@$_GET['download']) {
case 'resume_speed':
case 'noresume_speed':
case 'resume':
case 'noresume':
$object->set_byfile($FILENAME);
if ($_GET['download'] == 'noresume' || $_GET['download'] == 'noresume_speed') $object->use_resume = false;
if ($_GET['download'] == 'resume_speed' || $_GET['download'] == 'noresume_speed' ) $object->speed = 100;
$object->download();
break;
case 'data':
case 'dataresume':
$data = implode('' , file($FILENAME));
$object->set_bydata($data);
if ($_SERVER['download'] != 'dataresume') $object->use_resume = false;
$object->filename = basename($FILENAME);
$object->download();
break;
case 'auth':
$object->set_byfile($FILENAME);
$object->use_auth = true;
$object->handler['auth'] = "test_auth";
$object->download();
break;
case 'url':
$object->set_byurl('http://www.php.net/get/php_manual_chm.zip/from/cr.php.net/mirror');
$object->download();
break;
}

if ($object->bandwidth > 0)
{
error_reporting(E_NONE);
$b = intval(implode('',file('bandwidth.txt'))) + $object->bandwidth;
$f = fopen('bandwidth.txt','wb');
fwrite($f,$b);
fclose($f);
exit;
}

function test_auth($user,$pass) { //test authentication function
if ($user == 'user' && $pass == 'pass') return true;
return false;
}

?>

<head>
<style>
<!--
body { font-family: Tahoma; font-size: 12px }
a { color: #FF0000 }
-->
</style>
</head>

<title>HTTPDownload example</title>

<h2><font color="navy">HttpDownload</font></h2>Select a link and try it with a download manager (like <a href="http://reget.com">Reget</a>) .<br><br>

Total bandwidth used : <B><?=$bandwidth?></B>

<br><br>
<a href="test.php?download=noresume">Download file</a><br>
<a href="test.php?download=noresume_speed">Download file (speed limit 100 kbs)</a><br>
<a href="test.php?download=resume">Download file with resume</a><br>
<a href="test.php?download=resume_speed">Download file with resume (speed limit 100 kbs) </a><br>
<a href="test.php?download=data">Download file data (May slow)</a><br>
<a href="test.php?download=dataresume">Download file data with resume (May slow)</a><br>
<a href="test.php?download=auth">Authentication download (user/pass)</a><br>
<a href="test.php?download=url">URL Download (simple redirect)</a><br>

<p><font size="1"><font color="#808080">( Click
<a href="http://en.vietapi.com/wiki/index.php/PHP:_HttpDownload">
<font color="black">here</font></a><font color=""> to view class
information )</font></p>