-
Notifications
You must be signed in to change notification settings - Fork 4
/
SendFile.php
194 lines (169 loc) · 6.24 KB
/
SendFile.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
184
185
186
187
188
189
190
191
192
193
194
<?php
/**
* Sends a file for download
*
* @category Noginn
* @copyright Copyright (c) 2009 Tom Graham (http://www.noginn.com)
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Noginn_Controller_Action_Helper_SendFile extends Zend_Controller_Action_Helper_Abstract
{
/**
* Set cache headers
*
* @param array $options
*/
public function setCacheHeaders($options)
{
$response = $this->getResponse();
$cacheControl = array();
if (isset($options['public']) && $options['public']) {
$cacheControl[] = 'public';
}
if (isset($options['no-cache']) && $options['no-cache']) {
$cacheControl[] = 'no-cache';
}
if (isset($options['no-store']) && $options['no-store']) {
$cacheControl[] = 'no-store';
}
if (isset($options['must-revalidate']) && $options['must-revalidate']) {
$cacheControl[] = 'must-revalidate';
}
if (isset($options['proxy-validation']) && $options['proxy-validation']) {
$cacheControl[] = 'proxy-validation';
}
if (isset($options['max-age'])) {
$cacheControl[] = 'max-age=' . (int) $options['max-age'];
$response->setHeader('Expires', gmdate('r', time() + $options['max-age']), true);
}
if (isset($options['s-maxage'])) {
$cacheControl[] = 's-maxage=' . (int) $options['s-maxage'];
}
$response->setHeader('Cache-Control', implode(',', $cacheControl), true);
$response->setHeader('Pragma', 'public', true);
}
/**
* Validate the cache using the If-Modified-Since request header
*
* @param int $modified When the file was last modified as a unix timestamp
* @return bool
*/
public function notModifiedSince($modified)
{
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
// Send a 304 Not Modified header
$response = $this->getResponse();
$response->setHttpResponseCode(304);
$response->sendHeaders();
return true;
}
return false;
}
/**
* Send a file for download
*
* @param string $path Path to the file
* @param string $type The mime-type of the file
* @param array $options
* @return bool Whether the headers and file were sent
*/
public function sendFile($path, $type, $options = array())
{
$response = $this->getResponse();
if (!is_readable($path) || !$response->canSendHeaders()) {
return false;
}
// Set the cache-control
if (isset($options['cache'])) {
$this->setCacheHeaders($options['cache']);
}
// Get the last modified time
if (isset($options['modified'])) {
$modified = (int) $options['modified'];
} else {
$modified = filemtime($path);
}
// Validate the cache
if (!isset($options['cache']['no-store']) && $this->notModifiedSince($modified)) {
return true;
}
// Set the file name
if (isset($options['filename']) && !empty($options['filename'])) {
$filename = $options['filename'];
} else {
$filename = basename($path);
}
// Set the content disposition
if (isset($options['disposition']) && $options['disposition'] == 'inline') {
$disposition = 'inline';
} else {
$disposition = 'attachment';
}
$response->setHttpResponseCode(200);
$response->setHeader('Content-Type', $type, true);
$response->setHeader('Content-Disposition', $disposition . '; filename="' . $filename . '"', true);
// Do we want to use the X-Sendfile header or stream the file
if (isset($options['xsendfile']) && $options['xsendfile']) {
$response->setHeader('X-Sendfile', $path);
$response->sendHeaders();
return true;
}
$response->setHeader('Last-Modified', gmdate('r', $modified), true);
$response->setHeader('Content-Length', filesize($path), true);
$response->sendHeaders();
readfile($path);
return true;
}
/**
* Send file data as a download
*
* @param string $path Path to the file
* @param string $type The mime-type of the file
* @param string $filename The filename to send the file as, if null then use the base name of the path
* @param array $options
* @return bool Whether the headers and file were sent
*/
public function sendData($data, $type, $filename, $options = array())
{
$response = $this->getResponse();
if (!$response->canSendHeaders()) {
return false;
}
// Set the cache-control
if (isset($options['cache'])) {
$this->setCacheHeaders($options['cache']);
}
if (isset($options['modified'])) {
// Validate the cache
if (!isset($options['cache']['no-store']) && $this->notModifiedSince($options['modified'])) {
return true;
}
$response->setHeader('Last-Modified', gmdate('r', $options['modified']), true);
}
// Set the content disposition
if (isset($options['disposition']) && $options['disposition'] == 'inline') {
$disposition = 'inline';
} else {
$disposition = 'attachment';
}
$response->setHttpResponseCode(200);
$response->setHeader('Content-Type', $type, true);
$response->setHeader('Content-Disposition', $disposition . '; filename="' . $filename . '"', true);
$response->setHeader('Content-Length', strlen($data), true);
$response->sendHeaders();
echo $data;
return true;
}
/**
* Proxy method for sendFile
*
* @param string $path Path to the file
* @param string $type The mime-type of the file
* @param array $options
* @return bool Whether the headers and file were sent
*/
public function direct($path, $type, $options = array())
{
return $this->sendFile($path, $type, $options);
}
}