Rev 310 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
310 | daniel-mar | 1 | <?php |
2 | |||
3 | /* |
||
4 | * VtsBrowserDownload.class.php |
||
5 | * Copyright 2020 Daniel Marschall, ViaThinkSoft |
||
6 | * |
||
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||
8 | * you may not use this file except in compliance with the License. |
||
9 | * You may obtain a copy of the License at |
||
10 | * |
||
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
12 | * |
||
13 | * Unless required by applicable law or agreed to in writing, software |
||
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
||
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
16 | * See the License for the specific language governing permissions and |
||
17 | * limitations under the License. |
||
18 | */ |
||
19 | |||
20 | class VtsBrowserDownload { |
||
21 | |||
22 | private static function wellKnownInlineFile($file_extension) { |
||
23 | // Windows Firefox: Browser decided wheater to display or download by looking at the mime type (inline disposition with explicit filename works) |
||
24 | // Windows Chrome: Browser decided wheater to display or download by looking at the mime type (inline disposition with explicit filename DOES NOT WORK) |
||
25 | |||
26 | //$file_extension = strtolower($file_extension); |
||
27 | //$array_listen = array('txt', 'mp3', 'wav', 'mid', 'ogg', 'pdf', 'avi', 'mov', 'mp4', 'mpeg', 'mpg', 'swf', 'gif', 'jpg', 'jpeg', 'png'); |
||
28 | //return in_array($file_extension, $array_listen); |
||
29 | |||
30 | return true; |
||
31 | } |
||
32 | |||
33 | private static function getMimeType($file_extension) { |
||
34 | $file_extension = strtolower($file_extension); |
||
35 | return VtsFileTypeDetect::getMimeType('dummy.'.$file_extension); |
||
36 | } |
||
37 | |||
38 | public static function output_file($file, $mime_type='', $inline_mode=2/*2=auto*/) { |
||
39 | // Partitally taken from: |
||
40 | // - https://stackoverflow.com/a/13821992/488539 |
||
41 | // - https://stackoverflow.com/a/32885706/488539 |
||
42 | |||
43 | if (connection_status() != 0) return false; |
||
44 | |||
45 | $file_extension = pathinfo($file, PATHINFO_EXTENSION); |
||
46 | |||
47 | if(!is_readable($file)) throw new Exception('File not found or inaccessible!'); |
||
48 | $size = filesize($file); |
||
49 | $name = rawurldecode(basename($file)); |
||
50 | |||
51 | if ($mime_type == '') { |
||
52 | $mime_type = self::getMimeType($file_extension); |
||
53 | if (!$mime_type) $mime_type='application/force-download'; |
||
54 | } |
||
55 | |||
56 | |||
57 | |||
58 | while (ob_get_level() > 0) @ob_end_clean(); |
||
59 | |||
60 | switch ($inline_mode) { |
||
61 | |||
62 | case 0: |
||
63 | $disposition = 'attachment'; |
||
64 | break; |
||
65 | |||
66 | case 1: |
||
67 | $disposition = 'inline'; |
||
68 | break; |
||
69 | |||
70 | case 2: |
||
71 | $disposition = self::wellKnownInlineFile($file_extension) ? 'inline' : 'attachment'; |
||
72 | break; |
||
73 | |||
74 | default: |
||
75 | throw new Exception('Invalid value for inline_mode'); |
||
76 | break; |
||
77 | } |
||
78 | |||
79 | if(ini_get('zlib.output_compression')){ |
||
80 | ini_set('zlib.output_compression', 'Off'); |
||
81 | } |
||
82 | header('Content-Type: ' . $mime_type); |
||
83 | |||
311 | daniel-mar | 84 | $ua = isset($_SERVER['HTTP_USER_AGENT']) ? strtoupper($_SERVER['HTTP_USER_AGENT']) : ''; |
85 | if (strstr($ua, 'MSIE')) { |
||
310 | daniel-mar | 86 | $name_msie = preg_replace('/\./', '%2e', $name, substr_count($name, '.') - 1); |
87 | header('Content-Disposition: '.$disposition.';filename="'.$name_msie.'"'); |
||
311 | daniel-mar | 88 | } else if (strstr($ua, 'FIREFOX')) { |
89 | header('Content-Disposition: '.$disposition.';filename*="UTF-8\'\''.utf8_encode($name).'"'); |
||
310 | daniel-mar | 90 | } else { |
91 | // Note: There is possibly a bug in Google Chrome: https://stackoverflow.com/questions/61866508/chrome-ignores-content-disposition-filename |
||
92 | header('Content-Disposition: '.$disposition.';filename="'.$name.'"'); |
||
93 | } |
||
94 | |||
95 | header('Content-Transfer-Encoding: binary'); |
||
96 | header('Accept-Ranges: bytes'); |
||
97 | header('Cache-Control: public'); |
||
98 | |||
99 | if (isset($_SERVER['HTTP_RANGE'])) { |
||
100 | list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2); |
||
101 | list($range) = explode(",",$range,2); |
||
102 | list($range, $range_end) = explode("-", $range); |
||
103 | $range=intval($range); |
||
104 | if(!$range_end) { |
||
105 | $range_end=$size-1; |
||
106 | } else { |
||
107 | $range_end=intval($range_end); |
||
108 | } |
||
109 | |||
110 | $new_length = $range_end-$range+1; |
||
111 | header("HTTP/1.1 206 Partial Content"); |
||
112 | header("Content-Length: $new_length"); |
||
113 | header("Content-Range: bytes $range-$range_end/$size"); |
||
114 | } else { |
||
115 | $etag = md5_file($file); |
||
116 | header("Etag: $etag"); |
||
117 | if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && (trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)) { |
||
118 | header("HTTP/1.1 304 Not Modified"); |
||
119 | return true; |
||
120 | } |
||
121 | |||
122 | $new_length=$size; |
||
123 | header("Content-Length: ".$size); |
||
124 | header('Content-MD5: '.$etag); // RFC 2616 clause 14.15 |
||
125 | } |
||
126 | |||
127 | set_time_limit(0); |
||
128 | |||
129 | $chunksize = 1*(1024*1024); |
||
130 | $bytes_send = 0; |
||
131 | if ($file = fopen($file, 'r')) { |
||
132 | if(isset($_SERVER['HTTP_RANGE'])) |
||
133 | fseek($file, $range); |
||
134 | |||
135 | while(!feof($file) && |
||
136 | (!connection_aborted()) && // connection_status() == 0 |
||
137 | ($bytes_send<$new_length)) |
||
138 | { |
||
139 | $buffer = fread($file, $chunksize); |
||
140 | echo($buffer); |
||
141 | flush(); |
||
142 | $bytes_send += strlen($buffer); |
||
143 | } |
||
144 | fclose($file); |
||
145 | } else { |
||
146 | throw new Exception("Cannot open file $file"); |
||
147 | } |
||
148 | return((connection_status() == 0) and !connection_aborted()); |
||
149 | } |
||
150 | |||
151 | } |