diff --git a/CHANGELOG.md b/CHANGELOG.md index 68570da..64ed83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.2.0 - (unreleased) - Refactored MultipartStreamBuilder to clean up and allow injecting data without a filename +- Dynamically use memory or temp file to buffer the stream content. ## 1.1.2 - 2020-07-13 @@ -24,23 +25,23 @@ No changes from 0.2.0. ## 0.2.0 - 2017-02-20 You may do a BC update to version 0.2.0 if you are sure that you are not adding -multiple resources with the same name to the Builder. +multiple resources with the same name to the Builder. ### Fixed -- Make sure one can add resources with same name without overwrite. +- Make sure one can add resources with same name without overwrite. ## 0.1.6 - 2017-02-16 ### Fixed -- Performance improvements by avoid using `uniqid()`. +- Performance improvements by avoid using `uniqid()`. ## 0.1.5 - 2017-02-14 ### Fixed -- Support for non-readable streams. This fix was needed because flaws in Guzzle, Zend and Slims implementations of PSR-7. +- Support for non-readable streams. This fix was needed because flaws in Guzzle, Zend and Slims implementations of PSR-7. ## 0.1.4 - 2016-12-31 @@ -52,7 +53,7 @@ multiple resources with the same name to the Builder. ### Added -- Added `CustomMimetypeHelper` to allow you to configure custom mimetypes. +- Added `CustomMimetypeHelper` to allow you to configure custom mimetypes. ### Changed @@ -62,13 +63,13 @@ multiple resources with the same name to the Builder. ### Added -- Support for Outlook msg files. +- Support for Outlook msg files. ## 0.1.1 - 2016-08-10 ### Added -- Support for Apple passbook. +- Support for Apple passbook. ## 0.1.0 - 2016-07-19 diff --git a/src/MultipartStreamBuilder.php b/src/MultipartStreamBuilder.php index a14ab4e..6f94dbe 100644 --- a/src/MultipartStreamBuilder.php +++ b/src/MultipartStreamBuilder.php @@ -73,12 +73,10 @@ public function __construct($streamFactory = null) } /** - * Add a resource to the Multipart Stream + * Add a resource to the Multipart Stream. * - * @param string|resource|\Psr\Http\Message\StreamInterface $resource - * The filepath, resource or StreamInterface of the data. - * @param array $headers - * Additional headers array: ['header-name' => 'header-value']. + * @param string|resource|\Psr\Http\Message\StreamInterface $resource the filepath, resource or StreamInterface of the data + * @param array $headers additional headers array: ['header-name' => 'header-value'] * * @return MultipartStreamBuilder */ @@ -133,28 +131,40 @@ public function addResource($name, $resource, array $options = []) */ public function build() { - $streams = ''; + // Open a temporary read-write stream as buffer. + // If the size is less than predefined limit, things will stay in memory. + // If the size is more than that, things will be stored in temp file. + $buffer = fopen('php://temp', 'r+'); foreach ($this->data as $data) { // Add start and headers - $streams .= "--{$this->getBoundary()}\r\n". - $this->getHeaders($data['headers'])."\r\n"; + fwrite($buffer, "--{$this->getBoundary()}\r\n". + $this->getHeaders($data['headers'])."\r\n"); - // Convert the stream to string - /* @var $contentStream StreamInterface */ + /** @var $contentStream StreamInterface */ $contentStream = $data['contents']; + + // Read stream into buffer if ($contentStream->isSeekable()) { - $streams .= $contentStream->__toString(); + $contentStream->rewind(); // rewind to beginning. + } + if ($contentStream->isReadable()) { + while (!$contentStream->eof()) { + // Read 1MB chunk into buffer until reached EOF. + fwrite($buffer, $contentStream->read(1048576)); + } } else { - $streams .= $contentStream->getContents(); + fwrite($buffer, $contentStream->__toString()); } - - $streams .= "\r\n"; + fwrite($buffer, "\r\n"); } // Append end - $streams .= "--{$this->getBoundary()}--\r\n"; + fwrite($buffer, "--{$this->getBoundary()}--\r\n"); + + // Rewind to starting position for reading. + fseek($buffer, 0); - return $this->createStream($streams); + return $this->createStream($buffer); } /**