-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDownload.php
183 lines (161 loc) · 5.93 KB
/
Download.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<?php
namespace Util;
/**
* @package Util
* @summary 文件下载相关的通用方法.
*/
class Download
{
/**
* Method dlFileResumable
*
* @static
*
* @param string $file
* @param string $downloadName
* @param bool $isResume
*/
public static function dlFileResumable($file, $downloadName = null, $isResume = true)
{
// First, see if the file exists
if (!is_file($file)) {
static::notFoundError('');
}
// Gather relevent info about file
$size = filesize($file);
$fileInfo = pathinfo($file);
if (empty($downloadName)) {
// workaround for IE filename bug with multiple periods / multiple dots in filename
// that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe
$downloadName = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? preg_replace('/\./', '%2e', $fileInfo['basename'], substr_count($fileInfo['basename'], '.') - 1) : $fileInfo['basename'];
}
$fileExtension = strtolower($fileInfo['extension']);
//This will set the Content-Type to the appropriate setting for the file
switch ($fileExtension) {
case 'exe':
case 'rswk2':
$ctype = 'application/octet-stream';
break;
case 'zip':
$ctype = 'application/zip';
break;
case 'mp3':
$ctype = 'audio/mpeg';
break;
case 'mpg':
$ctype = 'video/mpeg';
break;
case 'avi':
$ctype = 'video/x-msvideo';
break;
default:
$ctype = 'application/force-download';
break;
}
//check if http_range is sent by browser (or download manager)
if ($isResume && isset($_SERVER['HTTP_RANGE'])) {
list($sizeUnit, $rangeOrig) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if (trim($sizeUnit) == 'bytes') {
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
list($range, $extra_ranges) = explode(',', $rangeOrig, 2);
} else {
$range = '';
}
} else {
$range = '';
}
//figure out download piece from range (if set)
list($seekStart, $seekEnd) = explode('-', $range, 2);
//set start and end based on range (if set), else set defaults
//also check for invalid ranges.
$seekEnd = (empty($seekEnd)) ? ($size - 1) : min(abs(intval($seekEnd)), ($size - 1));
$seekStart = (empty($seekStart) || $seekEnd < abs(intval($seekStart))) ? 0 : max(abs(intval($seekStart)), 0);
//add headers if resumable
if ($isResume) {
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seekStart > 0 || $seekEnd < ($size - 1)) {
header('HTTP/1.1 206 Partial Content');
}
header('Accept-Ranges: bytes');
header('Content-Range: bytes ' . $seekStart . '-' . $seekEnd . '/' . $size);
}
//headers for IE Bugs (is this necessary?)
//header("Cache-Control: cache, must-revalidate");
//header("Pragma: public");
header('Content-Type: ' . $ctype);
header('Content-Disposition: attachment; filename="' . $downloadName . '"');
header('Content-Length: ' . ($seekEnd - $seekStart + 1));
//open the file
$fp = fopen($file, 'rb');
//seek to start of missing part
fseek($fp, $seekStart);
//start buffered download
while (!feof($fp)) {
//reset time limit for big files
set_time_limit(0);
print(fread($fp, 1024 * 8));
flush();
ob_flush();
}
fclose($fp);
exit;
}
/**
* Method notFoundError
*
* @static
*
* @param string $error
*/
public static function notFoundError($error = '')
{
header('HTTP/1.1 404 Not Found');
die($error ? "<b>$error</b>" : '');
}
/**
* Add files and sub-directories in a folder to zip file.
*
* @param string $folder
* @param \ZipArchive $zipFile
* @param int $exclusiveLength Number of text to be exclusived from the file path.
*/
private static function folderToZip($folder, &$zipFile, $exclusiveLength)
{
$handle = opendir($folder);
while (false !== $f = readdir($handle)) {
if ($f != '.' && $f != '..') {
$filePath = "$folder/$f";
// Remove prefix from file path before add to zip.
$localPath = substr($filePath, $exclusiveLength);
if (is_file($filePath)) {
$zipFile->addFile($filePath, $localPath);
} elseif (is_dir($filePath)) {
// Add sub-directory.
$zipFile->addEmptyDir($localPath);
self::folderToZip($filePath, $zipFile, $exclusiveLength);
}
}
}
closedir($handle);
}
/**
* Zip a folder (include itself).
* Usage:
* HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
*
* @param string $sourcePath Path of directory to be zip.
* @param string $outZipPath Path of output zip file.
*/
public static function zipDir($sourcePath, $outZipPath)
{
$pathInfo = pathInfo($sourcePath);
$parentPath = $pathInfo['dirname'];
$dirName = $pathInfo['basename'];
$z = new \ZipArchive();
$z->open($outZipPath, \ZIPARCHIVE::CREATE);
$z->addEmptyDir($dirName);
self::folderToZip($sourcePath, $z, strlen("$parentPath/"));
$z->close();
}
}