PHP implementation of File download breakpoint continuation principle
PHP File Download
File download is the file from the server down, such as a file is Http://www.xxx.com/1.rar (the real existence of this file), directly in the browser input, can pop up the download dialog box, but if it is a picture address, it is probably not downloaded, Instead, it is opened directly with a browser. There is also to not let the client see the download address, it is best not to provide the download file address directly. There are, for example, only logged-in users to download. And so on. So I'm going to write a separate download program.
There are several response headers that are important to record
Content-type
Content-disposition
Content-length
Pragma
Cache-control
Content-type
Content-type tells the browser file MIME type, which is very important for a response header, mime variety, really too much
Content-disposition
Content-disposition is an extension of the MIME protocol, and the MIME protocol indicates how the MIME user agent displays additional files. When Internet Explorer receives the header, it activates the File Download dialog box , and its file name box automatically fills in the filename specified in the header. Yes, that's the head. Activate pop-up prompt download box
Because of the very many types of mime, it is likely that some MIME types will be omitted from the program. This way, the types are omitted, all with "Content-type:application/octet-stream" "Content-disposition: Attachment Filename=xxx.xx "(xx.xx as filename) after some browser testing found that the downloaded files are correct
Content-length
"Content-length:123" is to tell the browser that the size of this file is 123 bytes, in fact, I found that it seems not to set the header, the browser can also identify themselves
Pragma Cache-control
The 2 headers are set to public to tell the browser cache (in fact, for the cache header I have a lot of not understand clearly except public it means that all caches, but as far as the browser cache does not cache, may not necessarily, I tried a lot of browsers, no one is cached from the browser to read the file, But from the server up and down)
In the output file, I look at the online about 2 ways: One is a one-time output, the first is a segmented output (part of each output), I went to CSDN asked, others said segmented output, reduce the pressure on the server, because each time you read the file accounted for less memory
An example (there are few mime inside, more mime best to go online to find)
Class Download{var $file _name;var $file _dir;var $buffer _size = 1024;var $err = "";p ublic static $MIME _type = Array ("PDF" = "Application/pdf", "exe" = "Application/octet-stream", "zip" = "Application/zip", "Doc" and "Application" /msword "," xls "=" application/vnd.ms-excel "," ppt "=" application/vnd.ms-powerpoint "," gif "=" image/gif ", "png" = "image/png", "JPEG" and "JPG", "mp3" = "audio/mpeg", "wav" = "audio/x-wav", "mpeg" = "mpg", "MPE" =& gt; " Video/mpeg "," mov "=" video/quicktime "," avi "=" Video/x-msvideo ",);p ublic function __construct ($file _dir=" ", $ File_name= "") {$this->file_dir = $file _dir; $this->file_name = $file _name; $this->path = $file _dir. " /". $file _name; $this->suffix = PathInfo ($file _name,pathinfo_extension);} Public function down () {if (!file_exists ($this->path)) {$this->err = "The file was removed"; return false;} $content _type = $this->getmime ($this->suffix), $file _size = filesize ($this->path); Header ("Content-type:". $ CONTENT_TYPE);Header (' content-disposition:attachment; filename= '. $this->file_name. '); @header ("Cache-control:public"); @header ("Pragma:public"); Header ("Content-length:". $file _size); Ob_end_clean (); ReadFile ($this->path); Once read out $fp= fopen ($this->path, "R"); $buffer _size = $this->buffer_size; $cur _pos = 0; Record how much read while (!feof ($fp) && $file _size> $buffer _size+ $cur _pos) {$buffer = Fread ($fp, $buffer _size);// 1024-byte echo $buffer per read, $cur _pos + = $buffer _size;} Read the rest out because the file is under a very good $buffer = Fread ($fp, $file _size-$cur _pos); Echo $buffer; Fclose ($FP); return true;} Public Function getmime ($key = "") {if ($key = = "" "| |! Isset (self:: $MIME _type[$key]) {return "Application/octet-stream";} Return self:: $MIME _type[$key];}} $x = new DownLoad ($file _dir, $file _name); $file _dir path such as all $file _name filenames such as a.exe together is the whole path all/a.exe//$x->down ();
PHP Breakpoint continued to pass
The continuation of the breakpoint in this refers to the download time of the breakpoint continued to resume the breakpoint, upload the breakpoint continued to not understand the principle
Something to prepare.
Meaning of HTTP status code 206
Response header "Accept-ranges:bytes"
Response header "Content-range:bytes Start-end/all"
Fseek
Ob_flush
HTTP Status Code 206
The header of the request must contain a Range field that indicates the contents of the first byte to the first byte of the request. If the server supports it, it returns 206 Partial Content, and then uses the header's content-range to indicate the range and provides the data within the body within that range.
Accept-ranges:bytes
This field indicates whether the Web server supports a range (whether to support breakpoint continuation), returns Accept-ranges:bytes if supported, and returns Accept-ranges:none if not supported.
Content-range:
Used for the response header, which specifies the byte range of the returned Web resource. The format of this field value is: Start byte position-end byte position/web The total number of bytes of the resource, an example: content-range:1000-3000/5000
Fseek: This function moves the file pointer forward or backward from the current position to a new position, starting at the beginning of the file with the number of bytes measured
Ob_flush: This function moves the file pointer forward or backward from the current position to a new position, starting at the beginning of the file with the number of bytes measured
I tested the ie7,8 is not support for the continuation of the breakpoint (may be but I do not), the Apple browser does not. Ah ... (Fortunately Firefox Chrome Opera support)
This is how the breakpoint continues to pass.
First the client sends a request to PHP to download the file
At this point, because it is the first request, there is no range header inside the request header.
I grabbed the bag with fiddler.
Request Header
In PHP side $_server[' Http_range ' can take the rang inside the request header, because the first time there is no this, so we take the file content to take all, and then return the status code is also 200
Response header
when a part of the content is passed, click Pause, click on the almost continue, grab the packet's response to a range
When the PHP side returns, it returns the scope of the returned content Content-range:bytes 4194304-37939151/37939152 is the range of the returned data.
Accept-ranges:bytes indicates support for breakpoint continuation
The value inside the content-length:33744847 is based on the requested RANGE, which is $_server[' Http_range '], and the total size of the file, subtracted from the
In addition the HTTP status code is 206
Example
Class Download{var $file _path;var $file _name;var $file _suffix;var $speed = 1024;//indicates download speed 1mvar $err = ""; var $range _start= 0;var $range _end;var $http _range;var $file _size;public static $MIME _type = Array ("PDF" = "application/pdf", "EXE" =&G t; " Application/octet-stream "," zip "=" Application/zip "," Doc "and" Application/msword "," xls "=" application/ Vnd.ms-excel "," ppt "=" application/vnd.ms-powerpoint "," gif "=" image/gif "," png "=" image/png "," jpeg "= > "JPG", "MP3" and "Audio/mpeg", "wav" = "audio/x-wav", "mpeg" = "mpg", "mpe" = "video/mpeg", "mov" and "=" Video/quicktime "," avi "=" Video/x-msvideo ",);p ublic function __construct ($file _path=" ", $http _ranges=" ", $speed = "") {$this->file_path = $file _path; $this->file_name = basename ($file _path); $this->file_suffix = substr ( STRRCHR ($this->file_name, '. '), 1); $this->file_size= filesize ($file _path); $this->http_range = (Isset ($http _ranges) &&!empty ($http _ranges)! = "")? $http _ranges:false;! EmptY ($speed) && ($this->speed = $speed); }public function Down () {if (!file_exists ($this->file_path)) {$this->err = "The file is removed"; return false;} $file _path = $this->file_path; $content _type = $this->getmime ($this->file_suffix); $http _range = $this Http_range;if ($http _range) {//$http _range in the form of bytes=3145728-is almost like this $http_range = Preg_replace ('/[\s|,]*/', ' ', $ Http_range); $arr = explode ('-', substr ($http _range,6)); $this->range_start = (isset ($arr [0]) &&!empty ($arr [0])? $arr [0]: 0; $this->range_end = (Isset ($arr [1]) &&!empty ($arr [1]) && $arr [1]!=0]? $arr [1]: $this->file_size-1;} $this->setheader (); $fh = fopen ($file _path, "RB") fseek ($fh, $this->range_start); $speed = $this->speed;while (!feof ($FH)) {echo fread ($fh, 1024* $speed); Ob_flush (); sleep (1);} Fclose ($FH);} Public Function getmime ($key = "") {if ($key = = "" | |!isset (self:: $MIME _type[$key])) {return ' Application/octet-stream ';} Return self:: $MIME _type[$key];} Public functionSetHeader () {header (' cache-control:public '); header (' Content-type:application/octet-stream '); Header (' Content-disposition:attachment; Filename= '. $this->file_name), if ($this->http_range) {$range _start = $this->range_start; $range _end = $this- >range_end;header (' http/1.1 206 Partial Content '); header (' accept-ranges:bytes '); Header (sprintf (' Content-length :%u ', $range _end-$range _start)); Header (sprintf (' Content-range:bytes%s-%s/%s ', $range _start, $range _end, $this- file_size));} Else{header ("http/1.1 OK"), Header (sprintf (' Content-length:%s ', $this->file_size));}} $http _range = isset ($_server[' Http_range ')? $_server[' Http_range ']:false;//$x = new DownLoad ("All/1.exe", $http _ range);//$x->down ();