Skip to content

Commit 6676e7e

Browse files
committed
PHPLIB-244: Use strings instead of memory stream for GridFS upload buffering
1 parent 9dfb2c5 commit 6676e7e

File tree

3 files changed

+28
-31
lines changed

3 files changed

+28
-31
lines changed

src/GridFS/StreamWrapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public function stream_write($data)
150150
}
151151

152152
try {
153-
return $this->stream->insertChunks($data);
153+
return $this->stream->writeBytes($data);
154154
} catch (Exception $e) {
155155
trigger_error(sprintf('%s: %s', get_class($e), $e->getMessage()), \E_USER_WARNING);
156156
return false;

src/GridFS/WritableStream.php

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use MongoDB\BSON\ObjectId;
77
use MongoDB\BSON\UTCDateTime;
88
use MongoDB\Exception\InvalidArgumentException;
9+
use MongoDB\Exception\RuntimeException;
910

1011
/**
1112
* WritableStream abstracts the process of writing a GridFS file.
@@ -16,8 +17,7 @@ class WritableStream
1617
{
1718
private static $defaultChunkSizeBytes = 261120;
1819

19-
private $buffer;
20-
private $bufferLength = 0;
20+
private $buffer = '';
2121
private $chunkOffset = 0;
2222
private $chunkSize;
2323
private $collectionWrapper;
@@ -76,7 +76,6 @@ public function __construct(CollectionWrapper $collectionWrapper, $filename, arr
7676

7777
$this->chunkSize = $options['chunkSizeBytes'];
7878
$this->collectionWrapper = $collectionWrapper;
79-
$this->buffer = fopen('php://memory', 'w+b');
8079
$this->ctx = hash_init('md5');
8180

8281
$this->file = [
@@ -113,14 +112,10 @@ public function close()
113112
return;
114113
}
115114

116-
rewind($this->buffer);
117-
$cached = stream_get_contents($this->buffer);
118-
119-
if (strlen($cached) > 0) {
120-
$this->insertChunk($cached);
115+
if (strlen($this->buffer) > 0) {
116+
$this->insertChunkFromBuffer();
121117
}
122118

123-
fclose($this->buffer);
124119
$this->fileCollectionInsert();
125120
$this->isClosed = true;
126121
}
@@ -151,36 +146,31 @@ public function getSize()
151146
* Inserts binary data into GridFS via chunks.
152147
*
153148
* Data will be buffered internally until chunkSizeBytes are accumulated, at
154-
* which point a chunk's worth of data will be inserted and the buffer
155-
* reset.
149+
* which point a chunk document will be inserted and the buffer reset.
156150
*
157-
* @param string $toWrite Binary data to write
151+
* @param string $data Binary data to write
158152
* @return integer
159153
*/
160-
public function insertChunks($toWrite)
154+
public function writeBytes($data)
161155
{
162156
if ($this->isClosed) {
163157
// TODO: Should this be an error condition? e.g. BadMethodCallException
164158
return;
165159
}
166160

167-
$readBytes = 0;
161+
$bytesRead = 0;
168162

169-
while ($readBytes != strlen($toWrite)) {
170-
$addToBuffer = substr($toWrite, $readBytes, $this->chunkSize - $this->bufferLength);
171-
fwrite($this->buffer, $addToBuffer);
172-
$readBytes += strlen($addToBuffer);
173-
$this->bufferLength += strlen($addToBuffer);
163+
while ($bytesRead != strlen($data)) {
164+
$initialBufferLength = strlen($this->buffer);
165+
$this->buffer .= substr($data, $bytesRead, $this->chunkSize - $initialBufferLength);
166+
$bytesRead += strlen($this->buffer) - $initialBufferLength;
174167

175-
if ($this->bufferLength == $this->chunkSize) {
176-
rewind($this->buffer);
177-
$this->insertChunk(stream_get_contents($this->buffer));
178-
ftruncate($this->buffer, 0);
179-
$this->bufferLength = 0;
168+
if (strlen($this->buffer) == $this->chunkSize) {
169+
$this->insertChunkFromBuffer();
180170
}
181171
}
182172

183-
return $readBytes;
173+
return $bytesRead;
184174
}
185175

186176
private function abort()
@@ -206,22 +196,29 @@ private function fileCollectionInsert()
206196
return $this->file['_id'];
207197
}
208198

209-
private function insertChunk($data)
199+
private function insertChunkFromBuffer()
210200
{
211201
if ($this->isClosed) {
212202
// TODO: Should this be an error condition? e.g. BadMethodCallException
213203
return;
214204
}
215205

216-
$toUpload = [
206+
if (strlen($this->buffer) == 0) {
207+
return;
208+
}
209+
210+
$data = $this->buffer;
211+
$this->buffer = '';
212+
213+
$chunk = [
217214
'files_id' => $this->file['_id'],
218215
'n' => $this->chunkOffset,
219216
'data' => new Binary($data, Binary::TYPE_GENERIC),
220217
];
221218

222219
hash_update($this->ctx, $data);
223220

224-
$this->collectionWrapper->insertChunk($toUpload);
221+
$this->collectionWrapper->insertChunk($chunk);
225222
$this->length += strlen($data);
226223
$this->chunkOffset++;
227224
}

tests/GridFS/WritableStreamFunctionalTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ public function provideInvalidConstructorOptions()
5555
/**
5656
* @dataProvider provideInputDataAndExpectedMD5
5757
*/
58-
public function testInsertChunksCalculatesMD5($input, $expectedMD5)
58+
public function testWriteBytesCalculatesMD5($input, $expectedMD5)
5959
{
6060
$stream = new WritableStream($this->collectionWrapper, 'filename');
61-
$stream->insertChunks($input);
61+
$stream->writeBytes($input);
6262
$stream->close();
6363

6464
$fileDocument = $this->filesCollection->findOne(

0 commit comments

Comments
 (0)