|
12 | 12 | use Magento\Framework\App\Http\Context; |
13 | 13 | use Magento\Framework\App\PageCache\NotCacheableInterface; |
14 | 14 | use Magento\Framework\App\Request\Http as HttpRequest; |
| 15 | +use Magento\Framework\Exception\FileSystemException; |
15 | 16 | use Magento\Framework\Filesystem; |
16 | 17 | use Magento\Framework\Filesystem\Driver\File\Mime; |
17 | 18 | use Magento\Framework\Session\Config\ConfigInterface; |
|
24 | 25 | */ |
25 | 26 | class File extends Http implements NotCacheableInterface |
26 | 27 | { |
| 28 | + private const DEFAULT_RAW_CONTENT_TYPE = 'application/octet-stream'; |
| 29 | + |
27 | 30 | /** |
28 | 31 | * @var Http |
29 | 32 | */ |
@@ -83,49 +86,55 @@ public function __construct( |
83 | 86 | $this->filesystem = $filesystem; |
84 | 87 | $this->mime = $mime; |
85 | 88 | $this->options = array_merge($this->options, $options); |
| 89 | + if (!isset($this->options['filePath'])) { |
| 90 | + if (!isset($this->options['fileName'])) { |
| 91 | + throw new InvalidArgumentException("File name is required."); |
| 92 | + } |
| 93 | + $this->options['contentType'] ??= self::DEFAULT_RAW_CONTENT_TYPE; |
| 94 | + } |
86 | 95 | } |
87 | 96 |
|
88 | 97 | /** |
89 | 98 | * @inheritDoc |
90 | 99 | */ |
91 | 100 | public function sendResponse() |
92 | 101 | { |
93 | | - $dir = $this->filesystem->getDirectoryWrite($this->options['directoryCode']); |
94 | | - if (!isset($this->options['filePath'])) { |
95 | | - throw new InvalidArgumentException("File path is required."); |
96 | | - } |
97 | | - if (!$dir->isExist($this->options['filePath'])) { |
98 | | - throw new InvalidArgumentException("File '{$this->options['filePath']}' does not exists."); |
| 102 | + $dir = $this->filesystem->getDirectoryRead($this->options['directoryCode']); |
| 103 | + $forceHeaders = true; |
| 104 | + if (isset($this->options['filePath'])) { |
| 105 | + if (!$dir->isExist($this->options['filePath'])) { |
| 106 | + throw new InvalidArgumentException("File '{$this->options['filePath']}' does not exists."); |
| 107 | + } |
| 108 | + $filePath = $this->options['filePath']; |
| 109 | + $this->options['contentType'] ??= $dir->stat($filePath)['mimeType'] |
| 110 | + ?? $this->mime->getMimeType($dir->getAbsolutePath($filePath)); |
| 111 | + $this->options['contentLength'] ??= $dir->stat($filePath)['size']; |
| 112 | + $this->options['fileName'] ??= basename($filePath); |
| 113 | + } else { |
| 114 | + $this->options['contentLength'] = mb_strlen((string) $this->response->getContent(), '8bit'); |
| 115 | + $forceHeaders = false; |
99 | 116 | } |
100 | | - $filePath = $this->options['filePath']; |
101 | | - $contentType = $this->options['contentType'] |
102 | | - ?? $dir->stat($filePath)['mimeType'] |
103 | | - ?? $this->mime->getMimeType($dir->getAbsolutePath($filePath)); |
104 | | - $contentLength = $this->options['contentLength'] |
105 | | - ?? $dir->stat($filePath)['size']; |
106 | | - $fileName = $this->options['fileName'] |
107 | | - ?? basename($filePath); |
| 117 | + |
108 | 118 | $this->response->setHttpResponseCode(200); |
109 | | - $this->response->setHeader('Content-type', $contentType, true) |
110 | | - ->setHeader('Content-Length', $contentLength, true) |
111 | | - ->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"', true) |
112 | | - ->setHeader('Pragma', 'public', true) |
113 | | - ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) |
114 | | - ->setHeader('Last-Modified', date('r'), true); |
115 | | - |
116 | | - $this->response->sendHeaders(); |
117 | | - |
118 | | - if (!$this->request->isHead()) { |
119 | | - $stream = $dir->openFile($filePath, 'r'); |
120 | | - while (!$stream->eof()) { |
121 | | - // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput |
122 | | - echo $stream->read(1024); |
123 | | - } |
124 | | - $stream->close(); |
125 | | - if ($this->options['remove']) { |
126 | | - $dir->delete($filePath); |
| 119 | + $this->response->setHeader('Content-Type', $this->options['contentType'], $forceHeaders) |
| 120 | + ->setHeader('Content-Length', $this->options['contentLength'], $forceHeaders) |
| 121 | + ->setHeader( |
| 122 | + 'Content-Disposition', |
| 123 | + 'attachment; filename="' . $this->options['fileName'] . '"', |
| 124 | + $forceHeaders |
| 125 | + ) |
| 126 | + ->setHeader('Pragma', 'public', $forceHeaders) |
| 127 | + ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', $forceHeaders) |
| 128 | + ->setHeader('Last-Modified', date('r'), $forceHeaders); |
| 129 | + |
| 130 | + if (isset($this->options['filePath'])) { |
| 131 | + $this->response->sendHeaders(); |
| 132 | + if (!$this->request->isHead()) { |
| 133 | + $this->sendFileContent(); |
| 134 | + $this->afterFileIsSent(); |
127 | 135 | } |
128 | | - $this->response->clearBody(); |
| 136 | + } else { |
| 137 | + $this->response->sendResponse(); |
129 | 138 | } |
130 | 139 | return $this; |
131 | 140 | } |
@@ -155,4 +164,70 @@ public function clearHeader($name) |
155 | 164 | $this->response->clearHeader($name); |
156 | 165 | return $this; |
157 | 166 | } |
| 167 | + |
| 168 | + /** |
| 169 | + * @inheritDoc |
| 170 | + */ |
| 171 | + public function setBody($value) |
| 172 | + { |
| 173 | + $this->response->setBody($value); |
| 174 | + return $this; |
| 175 | + } |
| 176 | + |
| 177 | + /** |
| 178 | + * @inheritDoc |
| 179 | + */ |
| 180 | + public function appendBody($value) |
| 181 | + { |
| 182 | + $this->response->appendBody($value); |
| 183 | + return $this; |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * @inheritDoc |
| 188 | + */ |
| 189 | + public function getContent() |
| 190 | + { |
| 191 | + return $this->response->getContent(); |
| 192 | + } |
| 193 | + |
| 194 | + /** |
| 195 | + * @inheritDoc |
| 196 | + */ |
| 197 | + public function setContent($value) |
| 198 | + { |
| 199 | + $this->response->setContent($value); |
| 200 | + return $this; |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Sends file content to the client |
| 205 | + * |
| 206 | + * @return void |
| 207 | + * @throws FileSystemException |
| 208 | + */ |
| 209 | + private function sendFileContent(): void |
| 210 | + { |
| 211 | + $dir = $this->filesystem->getDirectoryRead($this->options['directoryCode']); |
| 212 | + $stream = $dir->openFile($this->options['filePath'], 'r'); |
| 213 | + while (!$stream->eof()) { |
| 214 | + // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput |
| 215 | + echo $stream->read(1024); |
| 216 | + } |
| 217 | + $stream->close(); |
| 218 | + } |
| 219 | + |
| 220 | + /** |
| 221 | + * Callback after file is sent to the client |
| 222 | + * |
| 223 | + * @return void |
| 224 | + * @throws FileSystemException |
| 225 | + */ |
| 226 | + private function afterFileIsSent(): void |
| 227 | + { |
| 228 | + if ($this->options['remove']) { |
| 229 | + $dir = $this->filesystem->getDirectoryWrite($this->options['directoryCode']); |
| 230 | + $dir->delete($this->options['filePath']); |
| 231 | + } |
| 232 | + } |
158 | 233 | } |
0 commit comments