Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

BinaryFileResponse.php

Zuletzt modifiziert: 02.04.2025, 15:02 - Dateigröße: 12.24 KiB


001  <?php
002   
003  /*
004   * This file is part of the Symfony package.
005   *
006   * (c) Fabien Potencier <fabien@symfony.com>
007   *
008   * For the full copyright and license information, please view the LICENSE
009   * file that was distributed with this source code.
010   */
011   
012  namespace Symfony\Component\HttpFoundation;
013   
014  use Symfony\Component\HttpFoundation\File\Exception\FileException;
015  use Symfony\Component\HttpFoundation\File\File;
016   
017  /**
018   * BinaryFileResponse represents an HTTP response delivering a file.
019   *
020   * @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de>
021   * @author stealth35 <stealth35-php@live.fr>
022   * @author Igor Wiedler <igor@wiedler.ch>
023   * @author Jordan Alliot <jordan.alliot@gmail.com>
024   * @author Sergey Linnik <linniksa@gmail.com>
025   */
026  class BinaryFileResponse extends Response
027  {
028      protected static $trustXSendfileTypeHeader = false;
029   
030      /**
031       * @var File
032       */
033      protected $file;
034      protected $offset = 0;
035      protected $maxlen = -1;
036      protected $deleteFileAfterSend = false;
037   
038      /**
039       * @param \SplFileInfo|string $file               The file to stream
040       * @param int                 $status             The response status code
041       * @param array               $headers            An array of response headers
042       * @param bool                $public             Files are public by default
043       * @param string|null         $contentDisposition The type of Content-Disposition to set automatically with the filename
044       * @param bool                $autoEtag           Whether the ETag header should be automatically set
045       * @param bool                $autoLastModified   Whether the Last-Modified header should be automatically set
046       */
047      public function __construct($file, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
048      {
049          parent::__construct(null, $status, $headers);
050   
051          $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
052   
053          if ($public) {
054              $this->setPublic();
055          }
056      }
057   
058      /**
059       * @param \SplFileInfo|string $file               The file to stream
060       * @param int                 $status             The response status code
061       * @param array               $headers            An array of response headers
062       * @param bool                $public             Files are public by default
063       * @param string|null         $contentDisposition The type of Content-Disposition to set automatically with the filename
064       * @param bool                $autoEtag           Whether the ETag header should be automatically set
065       * @param bool                $autoLastModified   Whether the Last-Modified header should be automatically set
066       *
067       * @return static
068       */
069      public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
070      {
071          return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
072      }
073   
074      /**
075       * Sets the file to stream.
076       *
077       * @param \SplFileInfo|string $file               The file to stream
078       * @param string              $contentDisposition
079       * @param bool                $autoEtag
080       * @param bool                $autoLastModified
081       *
082       * @return $this
083       *
084       * @throws FileException
085       */
086      public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
087      {
088          if (!$file instanceof File) {
089              if ($file instanceof \SplFileInfo) {
090                  $file = new File($file->getPathname());
091              } else {
092                  $file = new File((string) $file);
093              }
094          }
095   
096          if (!$file->isReadable()) {
097              throw new FileException('File must be readable.');
098          }
099   
100          $this->file = $file;
101   
102          if ($autoEtag) {
103              $this->setAutoEtag();
104          }
105   
106          if ($autoLastModified) {
107              $this->setAutoLastModified();
108          }
109   
110          if ($contentDisposition) {
111              $this->setContentDisposition($contentDisposition);
112          }
113   
114          return $this;
115      }
116   
117      /**
118       * Gets the file.
119       *
120       * @return File The file to stream
121       */
122      public function getFile()
123      {
124          return $this->file;
125      }
126   
127      /**
128       * Automatically sets the Last-Modified header according the file modification date.
129       */
130      public function setAutoLastModified()
131      {
132          $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
133   
134          return $this;
135      }
136   
137      /**
138       * Automatically sets the ETag header according to the checksum of the file.
139       */
140      public function setAutoEtag()
141      {
142          $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
143   
144          return $this;
145      }
146   
147      /**
148       * Sets the Content-Disposition header with the given filename.
149       *
150       * @param string $disposition      ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
151       * @param string $filename         Optionally use this UTF-8 encoded filename instead of the real name of the file
152       * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
153       *
154       * @return $this
155       */
156      public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
157      {
158          if ('' === $filename) {
159              $filename = $this->file->getFilename();
160          }
161   
162          if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
163              $encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
164   
165              for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
166                  $char = mb_substr($filename, $i, 1, $encoding);
167   
168                  if ('%' === $char || \ord($char) < 32 || \ord($char) > 126) {
169                      $filenameFallback .= '_';
170                  } else {
171                      $filenameFallback .= $char;
172                  }
173              }
174          }
175   
176          $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
177          $this->headers->set('Content-Disposition', $dispositionHeader);
178   
179          return $this;
180      }
181   
182      /**
183       * {@inheritdoc}
184       */
185      public function prepare(Request $request)
186      {
187          if (!$this->headers->has('Content-Type')) {
188              $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
189          }
190   
191          if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
192              $this->setProtocolVersion('1.1');
193          }
194   
195          $this->ensureIEOverSSLCompatibility($request);
196   
197          $this->offset = 0;
198          $this->maxlen = -1;
199   
200          if (false === $fileSize = $this->file->getSize()) {
201              return $this;
202          }
203          $this->headers->set('Content-Length', $fileSize);
204   
205          if (!$this->headers->has('Accept-Ranges')) {
206              // Only accept ranges on safe HTTP methods
207              $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
208          }
209   
210          if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
211              // Use X-Sendfile, do not send any content.
212              $type = $request->headers->get('X-Sendfile-Type');
213              $path = $this->file->getRealPath();
214              // Fall back to scheme://path for stream wrapped locations.
215              if (false === $path) {
216                  $path = $this->file->getPathname();
217              }
218              if ('x-accel-redirect' === strtolower($type)) {
219                  // Do X-Accel-Mapping substitutions.
220                  // @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect
221                  foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
222                      $mapping = explode('=', $mapping, 2);
223   
224                      if (2 === \count($mapping)) {
225                          $pathPrefix = trim($mapping[0]);
226                          $location = trim($mapping[1]);
227   
228                          if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
229                              $path = $location.substr($path, \strlen($pathPrefix));
230                              // Only set X-Accel-Redirect header if a valid URI can be produced
231                              // as nginx does not serve arbitrary file paths.
232                              $this->headers->set($type, $path);
233                              $this->maxlen = 0;
234                              break;
235                          }
236                      }
237                  }
238              } else {
239                  $this->headers->set($type, $path);
240                  $this->maxlen = 0;
241              }
242          } elseif ($request->headers->has('Range') && $request->isMethod('GET')) {
243              // Process the range headers.
244              if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
245                  $range = $request->headers->get('Range');
246   
247                  if (0 === strpos($range, 'bytes=')) {
248                      list($start, $end) = explode('-', substr($range, 6), 2) + [0];
249   
250                      $end = ('' === $end) ? $fileSize - 1 : (int) $end;
251   
252                      if ('' === $start) {
253                          $start = $fileSize - $end;
254                          $end = $fileSize - 1;
255                      } else {
256                          $start = (int) $start;
257                      }
258   
259                      if ($start <= $end) {
260                          $end = min($end, $fileSize - 1);
261                          if ($start < 0 || $start > $end) {
262                              $this->setStatusCode(416);
263                              $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
264                          } elseif ($end - $start < $fileSize - 1) {
265                              $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
266                              $this->offset = $start;
267   
268                              $this->setStatusCode(206);
269                              $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
270                              $this->headers->set('Content-Length', $end - $start + 1);
271                          }
272                      }
273                  }
274              }
275          }
276   
277          return $this;
278      }
279   
280      private function hasValidIfRangeHeader($header)
281      {
282          if ($this->getEtag() === $header) {
283              return true;
284          }
285   
286          if (null === $lastModified = $this->getLastModified()) {
287              return false;
288          }
289   
290          return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
291      }
292   
293      /**
294       * Sends the file.
295       *
296       * {@inheritdoc}
297       */
298      public function sendContent()
299      {
300          if (!$this->isSuccessful()) {
301              return parent::sendContent();
302          }
303   
304          if (0 === $this->maxlen) {
305              return $this;
306          }
307   
308          $out = fopen('php://output', 'wb');
309          $file = fopen($this->file->getPathname(), 'rb');
310   
311          stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
312   
313          fclose($out);
314          fclose($file);
315   
316          if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
317              unlink($this->file->getPathname());
318          }
319   
320          return $this;
321      }
322   
323      /**
324       * {@inheritdoc}
325       *
326       * @throws \LogicException when the content is not null
327       */
328      public function setContent($content)
329      {
330          if (null !== $content) {
331              throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
332          }
333   
334          return $this;
335      }
336   
337      /**
338       * {@inheritdoc}
339       */
340      public function getContent()
341      {
342          return false;
343      }
344   
345      /**
346       * Trust X-Sendfile-Type header.
347       */
348      public static function trustXSendfileTypeHeader()
349      {
350          self::$trustXSendfileTypeHeader = true;
351      }
352   
353      /**
354       * If this is set to true, the file will be unlinked after the request is sent
355       * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
356       *
357       * @param bool $shouldDelete
358       *
359       * @return $this
360       */
361      public function deleteFileAfterSend($shouldDelete)
362      {
363          $this->deleteFileAfterSend = $shouldDelete;
364   
365          return $this;
366      }
367  }
368