Skip to content

Commit a301644

Browse files
author
Divjot Arora
authored
GODRIVER-1770 Use chunkSize from files doc for download streams (#523)
1 parent 5ce8618 commit a301644

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

mongo/gridfs/bucket.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ const DefaultChunkSize int32 = 255 * 1024 // 255 KiB
3333
// ErrFileNotFound occurs if a user asks to download a file with a file ID that isn't found in the files collection.
3434
var ErrFileNotFound = errors.New("file with given parameters not found")
3535

36+
// ErrMissingChunkSize occurs when downloading a file if the files collection document is missing the "chunkSize" field.
37+
var ErrMissingChunkSize = errors.New("files collection document does not contain a 'chunkSize' field")
38+
3639
// Bucket represents a GridFS bucket.
3740
type Bucket struct {
3841
db *mongo.Database
@@ -381,14 +384,21 @@ func (b *Bucket) openDownloadStream(filter interface{}, opts ...*options.FindOpt
381384
}
382385

383386
if foundFile.Length == 0 {
384-
return newDownloadStream(nil, b.chunkSize, &foundFile), nil
387+
return newDownloadStream(nil, foundFile.ChunkSize, &foundFile), nil
388+
}
389+
390+
// For a file with non-zero length, chunkSize must exist so we know what size to expect when downloading chunks.
391+
if _, err := cursor.Current.LookupErr("chunkSize"); err != nil {
392+
return nil, ErrMissingChunkSize
385393
}
386394

387395
chunksCursor, err := b.findChunks(ctx, foundFile.ID)
388396
if err != nil {
389397
return nil, err
390398
}
391-
return newDownloadStream(chunksCursor, b.chunkSize, &foundFile), nil
399+
// The chunk size can be overridden for individual files, so the expected chunk size should be the "chunkSize"
400+
// field from the files collection document, not the bucket's chunk size.
401+
return newDownloadStream(chunksCursor, foundFile.ChunkSize, &foundFile), nil
392402
}
393403

394404
func deadlineContext(deadline time.Time) (context.Context, context.CancelFunc) {

mongo/integration/gridfs_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"time"
1616

1717
"go.mongodb.org/mongo-driver/bson"
18+
"go.mongodb.org/mongo-driver/bson/primitive"
1819
"go.mongodb.org/mongo-driver/event"
1920
"go.mongodb.org/mongo-driver/internal/testutil/assert"
2021
"go.mongodb.org/mongo-driver/internal/testutil/israce"
@@ -248,6 +249,48 @@ func TestGridFS(x *testing.T) {
248249
})
249250
}
250251
})
252+
mt.Run("chunk size determined by files collection document", func(mt *mtest.T) {
253+
// Test that the chunk size for a file download is determined by the chunkSize field in the files
254+
// collection document, not the bucket's chunk size.
255+
256+
bucket, err := gridfs.NewBucket(mt.DB)
257+
assert.Nil(mt, err, "NewBucket error: %v", err)
258+
defer func() { _ = bucket.Drop() }()
259+
260+
fileData := []byte("hello world")
261+
uploadOpts := options.GridFSUpload().SetChunkSizeBytes(4)
262+
fileID, err := bucket.UploadFromStream("file", bytes.NewReader(fileData), uploadOpts)
263+
assert.Nil(mt, err, "UploadFromStream error: %v", err)
264+
265+
// If the bucket's chunk size was used, this would error because the actual chunk size is 4 and the bucket
266+
// chunk size is 255 KB.
267+
var downloadBuffer bytes.Buffer
268+
_, err = bucket.DownloadToStream(fileID, &downloadBuffer)
269+
assert.Nil(mt, err, "DownloadToStream error: %v", err)
270+
271+
downloadedBytes := downloadBuffer.Bytes()
272+
assert.Equal(mt, fileData, downloadedBytes, "expected bytes %s, got %s", fileData, downloadedBytes)
273+
})
274+
mt.Run("error if files collection document does not have a chunkSize field", func(mt *mtest.T) {
275+
// Test that opening a download returns ErrMissingChunkSize if the files collection document has no
276+
// chunk size field.
277+
278+
oid := primitive.NewObjectID()
279+
filesDoc := bson.D{
280+
{"_id", oid},
281+
{"length", 10},
282+
{"filename", "filename"},
283+
}
284+
_, err := mt.DB.Collection("fs.files").InsertOne(mtest.Background, filesDoc)
285+
assert.Nil(mt, err, "InsertOne error for files collection: %v", err)
286+
287+
bucket, err := gridfs.NewBucket(mt.DB)
288+
assert.Nil(mt, err, "NewBucket error: %v", err)
289+
defer func() { _ = bucket.Drop() }()
290+
291+
_, err = bucket.OpenDownloadStream(oid)
292+
assert.Equal(mt, gridfs.ErrMissingChunkSize, err, "expected error %v, got %v", gridfs.ErrMissingChunkSize, err)
293+
})
251294
})
252295

253296
mt.RunOpts("bucket collection accessors", noClientOpts, func(mt *mtest.T) {

0 commit comments

Comments
 (0)