Skip to content

Commit 12bd4ac

Browse files
committed
PYTHON-1097 - Support GridFS custom file_id methods
1 parent e1850d8 commit 12bd4ac

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

gridfs/__init__.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,53 @@ def open_upload_stream(self, filename, chunk_size_bytes=None,
450450

451451
return GridIn(self._collection, **opts)
452452

453+
def open_upload_stream_with_id(
454+
self, file_id, filename, chunk_size_bytes=None, metadata=None):
455+
"""Opens a Stream that the application can write the contents of the
456+
file to.
457+
458+
The user must specify the file id and filename, and can choose to add
459+
any additional information in the metadata field of the file document
460+
or modify the chunk size.
461+
For example::
462+
463+
my_db = MongoClient().test
464+
fs = GridFSBucket(my_db)
465+
grid_in, file_id = fs.open_upload_stream(
466+
ObjectId(),
467+
"test_file",
468+
chunk_size_bytes=4,
469+
metadata={"contentType": "text/plain"})
470+
grid_in.write("data I want to store!")
471+
grid_in.close() # uploaded on close
472+
473+
Returns an instance of :class:`~gridfs.grid_file.GridIn`.
474+
475+
Raises :exc:`~gridfs.errors.NoFile` if no such version of
476+
that file exists.
477+
Raises :exc:`~ValueError` if `filename` is not a string.
478+
479+
:Parameters:
480+
- `file_id`: The id to use for this file. The id must not have
481+
already been used for another file.
482+
- `filename`: The name of the file to upload.
483+
- `chunk_size_bytes` (options): The number of bytes per chunk of this
484+
file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`.
485+
- `metadata` (optional): User data for the 'metadata' field of the
486+
files collection document. If not provided the metadata field will
487+
be omitted from the files collection document.
488+
"""
489+
validate_string("filename", filename)
490+
491+
opts = {"_id": file_id,
492+
"filename": filename,
493+
"chunk_size": (chunk_size_bytes if chunk_size_bytes
494+
is not None else self._chunk_size_bytes)}
495+
if metadata is not None:
496+
opts["metadata"] = metadata
497+
498+
return GridIn(self._collection, **opts)
499+
453500
def upload_from_stream(self, filename, source, chunk_size_bytes=None,
454501
metadata=None):
455502
"""Uploads a user file to a GridFS bucket.
@@ -488,6 +535,43 @@ def upload_from_stream(self, filename, source, chunk_size_bytes=None,
488535

489536
return gin._id
490537

538+
def upload_from_stream_with_id(self, file_id, filename, source,
539+
chunk_size_bytes=None, metadata=None):
540+
"""Uploads a user file to a GridFS bucket with a custom file id.
541+
542+
Reads the contents of the user file from `source` and uploads
543+
it to the file `filename`. Source can be a string or file-like object.
544+
For example::
545+
546+
my_db = MongoClient().test
547+
fs = GridFSBucket(my_db)
548+
file_id = fs.upload_from_stream(
549+
ObjectId(),
550+
"test_file",
551+
"data I want to store!",
552+
chunk_size_bytes=4,
553+
metadata={"contentType": "text/plain"})
554+
555+
Raises :exc:`~gridfs.errors.NoFile` if no such version of
556+
that file exists.
557+
Raises :exc:`~ValueError` if `filename` is not a string.
558+
559+
:Parameters:
560+
- `file_id`: The id to use for this file. The id must not have
561+
already been used for another file.
562+
- `filename`: The name of the file to upload.
563+
- `source`: The source stream of the content to be uploaded. Must be
564+
a file-like object that implements :meth:`read` or a string.
565+
- `chunk_size_bytes` (options): The number of bytes per chunk of this
566+
file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`.
567+
- `metadata` (optional): User data for the 'metadata' field of the
568+
files collection document. If not provided the metadata field will
569+
be omitted from the files collection document.
570+
"""
571+
with self.open_upload_stream_with_id(
572+
file_id, filename, chunk_size_bytes, metadata) as gin:
573+
gin.write(source)
574+
491575
def open_download_stream(self, file_id):
492576
"""Opens a Stream from which the application can read the contents of
493577
the stored file specified by file_id.

test/test_gridfs_bucket.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import gridfs
2424

2525
from bson.binary import Binary
26+
from bson.objectid import ObjectId
2627
from bson.py3compat import StringIO, string_type
2728
from gridfs.errors import NoFile, CorruptGridFile
2829
from pymongo.errors import (ConfigurationError,
@@ -270,14 +271,38 @@ def test_get_version(self):
270271
self.assertRaises(NoFile, self.fs.open_download_stream_by_name,
271272
"test", revision=-4)
272273

273-
def test_upload_from_stream_filelike(self):
274+
def test_upload_from_stream(self):
274275
oid = self.fs.upload_from_stream("test_file",
275276
StringIO(b"hello world"),
276277
chunk_size_bytes=1)
277278
self.assertEqual(11, self.db.fs.chunks.count())
278279
self.assertEqual(b"hello world",
279280
self.fs.open_download_stream(oid).read())
280281

282+
def test_upload_from_stream_with_id(self):
283+
oid = ObjectId()
284+
self.fs.upload_from_stream_with_id(oid,
285+
"test_file_custom_id",
286+
StringIO(b"custom id"),
287+
chunk_size_bytes=1)
288+
self.assertEqual(b"custom id",
289+
self.fs.open_download_stream(oid).read())
290+
291+
def test_open_upload_stream(self):
292+
gin = self.fs.open_upload_stream("from_stream")
293+
gin.write(b"from stream")
294+
gin.close()
295+
self.assertEqual(b"from stream",
296+
self.fs.open_download_stream(gin._id).read())
297+
298+
def test_open_upload_stream_with_id(self):
299+
oid = ObjectId()
300+
gin = self.fs.open_upload_stream_with_id(oid, "from_stream_custom_id")
301+
gin.write(b"from stream with custom id")
302+
gin.close()
303+
self.assertEqual(b"from stream with custom id",
304+
self.fs.open_download_stream(oid).read())
305+
281306
def test_missing_length_iter(self):
282307
# Test fix that guards against PHP-237
283308
self.fs.upload_from_stream("empty", b"")

0 commit comments

Comments
 (0)