[CSHARP-947] Programming with MongoGridFS and chunks in C# Created: 07/Apr/14  Updated: 05/Apr/19  Resolved: 08/Apr/14

Status: Closed
Project: C# Driver
Component/s: Documentation
Affects Version/s: 1.9
Fix Version/s: None

Type: Task Priority: Major - P3
Reporter: Anatoly Molotkov [X] Assignee: Unassigned
Resolution: Done Votes: 0
Labels: question
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
is duplicated by CSHARP-949 Follow up on CSHARP-947 Closed

 Description   

Hello.

I've been stuck for a couple of days trying to save a Chunk. I have not been able to find a single sample of this anywhere in your documentation, or online. Can you please point to a good resource?

I have an object in memory. I need to wrap something with an ID around it and save it to a chunk. I've done that with a wrapper class, and the code to save seems to work. On deserializing, the query for that ID returns null. I have not found any samples of saving and retrieving a chunk anywhere.

Thank you - any suggestions would be appreciated.

My code to save the chunk:

var connectionString = Properties.Settings.Default.DBConnectionString ;
var client = new MongoClient(connectionString);
var server = client.GetServer();
var database = server.GetDatabase(DBName);
var gfsPhoto = new MongoGridFS(server, DBName, new MongoGridFSSettings());
var MC = new MongoChunk(Value);
gfsPhoto.Chunks.Save(MC);

MongoChunk has two properties:

public Guid Id;
public object Value;

I'm trying this to retrieve the data, but Ch returns null:

var gfsPhoto = new MongoGridFS(server, DBName, new MongoGridFSSettings());
var Ch = gfsPhoto.Chunks.FindOne(Query.EQ("_id", MC.ID));
CP.Value = Ch.GetValue(1); //not sure if this line is correct either - it's not hit due to the current error

Thank you,

Anatoly



 Comments   
Comment by Anatoly Molotkov [X] [ 09/Apr/14 ]

Robert, one issue I ran into is strange. I am able to save and restore, and the image appears to be correct. However, whenever I hit Save again, I get this error on the line bitmap.Save(stream, ImageFormat.Bmp);. I'm curious what I should review about the image restored back from the db to identify the cause. The error is:

System.Runtime.InteropServices.ExternalException (0x80004005): A generic error occurred in GDI+. \r\n at System.Drawing.Image.Save (Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)\r\n

Thank you!

Comment by Robert Stam [ 08/Apr/14 ]

You're welcome. Glad you were able to adapt the sample code to your needs.

Comment by Anatoly Molotkov [X] [ 08/Apr/14 ]

Robert, thanks so much. I was able to adapt this to my needs. I would suggest you folks make C# driver reference available or easier to find, as the Internet is full of old/outdated Mongo samples, yet one can't find the right ones. However, I'm impressed with your quick response. My client is planning to adopt Mongo for our product when it becomes commercial.

Regards,

Anatoly

Comment by Robert Stam [ 07/Apr/14 ]

I see... as you have discovered a single document cannot exceed 16MB, so the solution would be to store the bitmap separately (as a GridFS file) and then store a reference to the bitmap in your document. For example, you class would more or less look like this:

public class C
{
    public int Id { get; set; }
    public ObjectId BitmapId { get; set; }
    // other members
}

To save both the document and its related bitmap to the database, you would first upload the bitmap and then store a reference to it in your document. You could use helper methods like InsertDocumentAndBitmap and UploadBitmap like these:

public static void InsertDocumentAndBitmap(MongoCollection<C> collection, C document, Bitmap bitmap)
{
    document.BitmapId = UploadBitmap(collection.Database.GridFS, bitmap);
    collection.Insert(document);
}
 
public static ObjectId UploadBitmap(MongoGridFS gridFS, Bitmap bitmap)
{
    using (var stream = new MemoryStream())
    {
        bitmap.Save(stream, ImageFormat.Bmp);
        stream.Position = 0;
        var bitmapId = ObjectId.GenerateNewId();
        var remoteFilename = bitmapId.ToString();
        var createOptions = new MongoGridFSCreateOptions { Id = bitmapId };
        var fileInfo = gridFS.Upload(stream, remoteFilename, createOptions);
        return bitmapId;
    }
}

And you would read the document back from the database with helper methods like:

public static Tuple<C, Bitmap> FindDocumentAndBitmap(MongoCollection<C> collection, int id)
{
    var document = collection.FindOne(Query.EQ("_id", id));
    if (document == null)
    {
        return null;
    }
 
    Bitmap bitmap = null;
    if (document.BitmapId != ObjectId.Empty)
    {
        bitmap = DownloadBitmap(collection.Database.GridFS, document.BitmapId);
    }
 
    return new Tuple<C, Bitmap>(document, bitmap);
}
 
public static Bitmap DownloadBitmap(MongoGridFS gridFS, ObjectId bitmapId)
{
    var fileInfo = gridFS.FindOneById(bitmapId);
    if (fileInfo == null)
    {
        return null;
    }
 
    using (var stream = new MemoryStream())
    {
        gridFS.Download(stream, fileInfo);
        stream.Position = 0;
        return new Bitmap(stream);
    }
}

You don't have to read the document and download the bitmap at the same time... you could just read the document and only download the bitmap later when and if you actually need it.

Comment by Anatoly Molotkov [X] [ 07/Apr/14 ]

Hi Robert,

I'm saving a property of type Bitmap, which might be over 16 Mb. In any case, I was getting a deserializlizing error when trying to save an object with a Bitmap member. So, I am trying to save an IN-MEMORY object (Bitmap), not a file on disk. How would I save and retrieve it? I'll have many, potentially (one for each "parent" record). I thought Chunks were used for working in-memory.

Thank you,

Anatoly

Comment by Robert Stam [ 07/Apr/14 ]

You only need to use GridFS for storing very large binary blobs in the database, and even then, you wouldn't work at the chunk level but at the level of the entire file. For example, to upload a GridFS file:

var gridFS = database.GridFS;
var localFilename = @"C:\temp\somefile";
var remoteFilename = "somefile";
var fileInfo = gridFS.Upload(localFilename, remoteFilename);

To read the file back, you download the entire file, not a chunk at a time:

gridFS.Download(localFilename, remoteFilename);

There are other overloads of Upload and Download that let you work with Streams instead of files on disk, or for downloading a particular version of a GridFS file (the default is to download the most recent version if there is more than one version).

Let me know if I have misunderstood your question and we can follow up.

Generated at Wed Feb 07 21:38:16 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.