|
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