MongoDB C# How to Update GridFS File and metadata? - vb.net

Need a solution to to see if the grid file exist and then update the file with metadata.
I am using the solution below. but want a better one.
Don't mind having separate Update and Delete methods.
Thanks
Public Class storedXYZ
Property Data As Stream
Property MetaData As storedXYZMetaData
End Class
Public Sub SaveStoredXYZ(storedXYZ As StoredXYZ)
Dim MongoGridFSCreateOptions As New MongoDB.Driver.GridFS.MongoGridFSCreateOptions
Dim qry As IMongoQuery
qry = Query.EQ("metadata.StoredXYZId", BsonValue.Create(storedXYZ.MetaData.StoredXYZ.ToString()))
Dim gridFile As MongoGridFSFileInfo = mdbGridFS.FindOne(qry)
If gridFile IsNot Nothing Then
Dim mongoStream As MongoGridFSStream
MongoGridFSCreateOptions.Metadata = storedXYZ.MetaData.ToBsonDocument
mongoStream = gridFile.OpenWrite()
''Convert MongoStream to MemoryStream
Dim fs As Stream = New MemoryStream()
Dim buffer As Byte() = New Byte(9999) {}
Dim bytesRead As Integer = 0
Do
bytesRead = storedXYZ.Data.Read(buffer, 0, buffer.Length)
mongoStream.Write(buffer, 0, bytesRead)
Loop While bytesRead > 0
mongoStream.Seek(0, SeekOrigin.Begin)
mongoStream.Position = 0
mdbGridFS.SetMetadata(gridFile, storedAXYZ.MetaData.ToBsonDocument)
Else
MongoGridFSCreateOptions.Metadata = storedXYZ.MetaData.ToBsonDocument
Dim fileinfo As MongoGridFSFileInfo
fileinfo = mdbGridFS.Upload(storedXYZ.Data, storedXYZ.MetaData.Name, MongoGridFSCreateOptions)
End If
End Sub

Rather than trying to overwrite the current file why not just fire off a delete command and then re-upload the file and update the document to the old ones Id?

MongoGridFS Upload method gets a new version of the file when you upload a file using an existing file name.
Using OpenWrite with an existing file name updates the file in place. The C# driver is the only driver that allows to update a GridFS file in place.
More information here

You can use fs.files and fs.chnuks collections as any other collection but you have to be careful to keep important for GridFS data untouched.

Related

Move binary data until specific values from one file to another

I writing a Visual Basic app and stuck at one point:
I need that app read the file, select everything before DDS string, cut it from file and paste to new file.
Then after edit a DDS insert that header.
Problem is, this header before DDS have not fixed length: every file of this type have different header. I tried to mess with System.IO.FileStream but got no result.
Is this even possible to do?
The header length is not that much, a simple search pattern is probably enough.
Pass the sequence of Bytes to find inside the File and the File path to the FindHeader method.
It returns a byte array containing all the bytes collected before the specified sequence is found.
This is a simple patter matching that seeks forward until it finds the first byte that can match the specified sequence.
It then reads a buffer and compares the buffer with the sequence:
- if it's a match, returns the bytes accumulated until that point;
- if it's not, it backtracks from the current position of a [Sequence Length] - 1 positions (inside the current Stream buffer) and continues.
You can call it like this:
Dim closeSequence = New Byte() { &H44, &H44, &H53 }
Dim headerBytes = FindHeader([Source File 1 Path], closeSequence)
Now we have the Header of the first source file.
The data section of the second source file is then:
Dim sourceFile2DataStart = FindHeader([Source File 2 Path], closeSequence).Length + closeSequence.Length
Dim dataLength = New FileInfo([Source File 2 Path]).Length - sourceFile2DataStart
We need to create a third file which will contain the Header of the fist file and the data read from the second file.
' Create a read buffer. The buffer length is less than or equal to the data length
Dim bufferLength As Integer = CInt(If(dataLength >= 1024, 1024, dataLength))
Dim buffer As Byte() = New Byte(bufferLength - 1) {}
Dim read As Integer = 0
Using two FileStream objects, we create a new Destination File, write the header of the first Source File, the closeSequence that identifies the start of the data section, then we read a buffer from the second Source File and write the buffer to the Destination File:
Dim patchworkFilePath as string = [Path of the Destination File]
Using sWriter As FileStream = New FileStream(patchworkFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None),
sReader As FileStream = New FileStream([Source File 2 Path], FileMode.Open, FileAccess.Read, FileShare.None)
sReader.Seek(sourceFile2DataStart, SeekOrigin.Begin)
sWriter.Write(header1Bytes, 0, header1Bytes.Length)
sWriter.Write(closeSequence, 0, closeSequence.Length)
While True
read = sReader.Read(buffer, 0, buffer.Length)
If read = 0 Then Exit While
sWriter.Write(buffer, 0, read)
End While
End Using
The Header reader method:
Public Function FindHeader(filePath As String, headerClosure As Byte()) As Byte()
Dim byteToFind = headerClosure(0)
Dim buffer = New Byte(headerClosure.Length - 1) {}
Dim header = New List(Of Byte)(2048)
Dim read As Integer = 0
Using fs As FileStream = New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)
While fs.Position <= (fs.Length - headerClosure.Length)
read = fs.ReadByte()
If read = byteToFind Then
fs.Read(buffer, 1, buffer.Length - 1)
buffer(0) = CByte(read)
If buffer.SequenceEqual(headerClosure) Then Exit While
fs.Seek(-(buffer.Length - 1), SeekOrigin.Current)
End If
header.Add(CByte(read))
End While
End Using
Return header.ToArray()
End Function

No working append byte array to existing file using visual Basic

I have two strings, each string is a PDF etiquette, I must to write this two etiquette into a PDF file. To do this I convert a each string to a byte array (I don't know if this is the best way) and each I Write into a PDF file. When I write one etiquette into PDF file all is good, I see the etiquette, but then I try to appending the second, the result is the same, into the file is only the first etiquette. For example this code write first etiquette and all working good:
Dim fs As FileStream = New FileStream(fullFileName, FileMode.CreateNew)
fs.Close()
fs = New FileStream(fullFileName, FileMode.Append)
Dim str As String = GetPDFString(27)
Dim binaryData As Byte() = ConvertStringToByte(str)
fs.Write(binaryData, 0, binaryData.Length)
fs.Close()
but if I want to append the second etiquette in the same PDF file using this code ... this not appending.
Dim fs As FileStream = New FileStream(fullFileName, FileMode.CreateNew)
fs.Close()
fs = New FileStream(fullFileName, FileMode.Append)
Dim str As String = GetPDFString(25)
Dim str1 As String = GetPDFString(27)
Dim binaryData As Byte() = ConvertStringToByte(str)
Dim binaryData1 As Byte() = ConvertStringToByte(str1)
fs.Write(binaryData, 0, binaryData.Length)
fs.Write(binaryData1, 0, binaryData1.Length)
fs.Close()
both have the same result, and I don't understand why the second etiquette isn't appending? Thank you a lot.
Your question title suggests that you are asking about how to append a byte to a FileStream, not about PDF, and not about Base64 string conversion (which you are using in your code).
Before asking a question on StackOverflow, you need to ensure you are conveying only one problem at a time. Remove everything that is not relevant, and prepare a code sample we can use in a brand new VS project, in order to reproduce your problem and help you solve it.
Now, if your question is really about appending a byte (or a byte array) to a file, it's as simple as one line of code (or two, if you keep FileStream approach). See below link:
C# Append byte array to existing file
Also copy-pasted for your convenience here (and converted from C# to VB.NET):
Dim appendMe As Byte() = New Byte(999) {}
File.AppendAllBytes("C:\test.exe", appendMe)
Or, to avoid memory overflow, if your byte array is expected to be large enough:
Public Shared Sub AppendAllBytes(path As String, bytes As Byte())
'argument-checking here.
Using stream = New FileStream(path, FileMode.Append)
stream.Write(bytes, 0, bytes.Length)
End Using
End Sub
With this line:
fs.Write(binaryData1, binaryData.Length + 1, binaryData1.Length)
Specifically the second argument (binaryData.Length + 1), you're telling it to start appending from the wrong position of binaryData1. If it's 3 bytes long, for example, and so is binaryData, it won't append anything. It should be similar to the first .Write line:
fs.Write(binaryData1, 0, binaryData1.Length)
So it appends all of binaryData1. It will still append it after binaryData - you don't need to specify the length of the preceeding binaryData in this line.
Alternatively, bypassing the above entirely, concatenate your two strings before encoding/writing them to the file:
Dim fs As FileStream = New FileStream(fullFileName, FileMode.CreateNew)
fs.Close()
fs = New FileStream(fullFileName, FileMode.Append)
Dim str As String = GetPDFString(id, token, depot, 25)
Dim str1 As String = GetPDFString(id, token, depot, 27)
Dim binaryData As Byte() = Convert.FromBase64String(str & str1) 'concatenate strings
fs.Write(binaryData, 0, binaryData.Length)
fs.Close()

How to get single file from zip file?

I want to get just one file from .zip file in VB.NET. I don't need to extract all of .zip file, just one file.
I'm working with framework 4.5.
.NET Framework 4.5 has ZipFile class which can do this for you. This code should get you started:
Dim zipPath As String = "Sample.zip"
Using archive = ZipFile.Open(zipPath, ZipArchiveMode.Read)
Dim entry = archive.GetEntry("MyFile.pdf")
Using reader As New BinaryReader(entry.Open())
System.IO.File.WriteAllBytes("MyFile.pdf", ReadAllBytes(reader))
End Using
End Using
ReadAllBytes() is a helper method that fetches all bytes from a binary stream:
Public Shared Function ReadAllBytes(reader As BinaryReader) As Byte()
Const bufferSize As Integer = 4096
Using ms As New MemoryStream()
Dim buffer(bufferSize) As Byte
Dim count As Integer
Do
count = reader.Read(buffer, 0, buffer.Length)
If count > 0 Then ms.Write(buffer, 0, count)
Loop While count <> 0
Return ms.ToArray()
End Using
End Function
Make sure you're using .NET Framework 4.5 or above and that you have included references to System.IO.Compression and System.IO.Compression.FileSystem.
try with this code with the help of DotNetZip
Using zip As ZipFile = ZipFile.Read(ExistingZipFile)
Dim e As ZipEntry = zip("DocumentToFind.txt")
e.Extract(OutputStream)
End Using
otherwise you can use ZipArchiveClass in this way
Using zip As ZipArchive = ZipFile.Open(zipfile, ZipArchiveMode.Read)
Dim file = zip.Entries.Where(Function(x) x.Name = "fileToFind")
If file IsNot Nothing Then
file.ExtractToFile("yourFile")
End If
End Using
This will allow you to read txt files from a zip line by line
Dim zipPath As String = "ZIP FILE LOCATION"
Using zipStream = New FileStream(last_pafx23_open, FileMode.Open)
Using archive = New ZipArchive(zipStream, ZipArchiveMode.Read)
For Each ent In archive.Entries
MsgBox(ent.ToString)
Using stream = ent.Open()
Using reader = New StreamReader(stream)
While Not reader.EndOfStream
MsgBox(reader.ReadLine)
End While
End Using
End Using
Next
End Using
End Using
Skip the BinaryReader w/ ReadAllBytes() helper function, use ExtractToFile() instead:
Imports System.IO.Compression
Using archive = ZipFile.Open("Sample.zip", ZipArchiveMode.Read)
Dim entry = archive.GetEntry("MyFile.pdf")
If entry IsNot Nothing then entry.ExtractToFile("MyFile.pdf")
End Using
Still needs the references to System.IO.Compression and System.IO.Compression.FileSystem, of course.

Put content of fileStream in dataset

I want to read Stream that i get from XtrapivotGrid of DevExpress. I can save it in the computer but what i want is to save it in one of my table in my dataset called dataset1.
For now i have that code who permit to save it the directory Temp:.
Using FS As New IO.FileStream("D:\Temp\qqc.layout", IO.FileMode.Create)
PivotGridControl1.SaveLayoutToStream(FS)
End Using
Dim read As New System.IO.FileStream("D:\Temp\qqc.layout", IO.FileMode.Open, IO.FileAccess.Read)
DataSet1.LayoutMainRapport.ReadXml(read)
DataSet1.AcceptChanges()
read.Close()
The table LayoutMainRapport have 3 columns:
ID(Int)
Name(nvarchar(50))
Content(xml).
The output from the stream is xml.
thanks in advance!
I believe you should convert your saved layout into string and then save this string to database (and of course you can load this layout back from the database string value):
Private Function SaveLayoutToString(ByVal dxControl As DevExpressControl) As String
Using ms As MemoryStream = New MemoryStream()
dxControl.SaveLayoutToStream(ms)
Return Convert.ToBase64String(ms.ToArray())
End Using
End Function
Private Sub RestoreLayoutFromString(ByVal dxControl As DevExpressControl, ByVal layout As String)
If String.IsNullOrEmpty(layout) Then
Return
End If
Using ms As MemoryStream = New MemoryStream(Convert.FromBase64String(layout))
dxControl.RestoreLayoutFromStream(ms)
End Using
End Sub
Here DevExpressControl dxControl is the DevExpress control which supports saving and loading layout (XtraPivotGrid, XtraGrid, XtraLayoutControl etc.)
I simply had to save a name for the xml and after save it into my dataset.
Dim saveDialog As SaveLayout = New SaveLayout
If saveDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
Dim read As New IO.MemoryStream
PivotGridControl1.SaveLayoutToStream(read)
read.Position = 0
Dim lecteur As New IO.StreamReader(read)
Dim ligne As DataRow = DataSet1.LayoutMainRapport.NewRow()
ligne("Nom") = saveDialog.txtNom.Text
ligne("Contenu") = lecteur.ReadToEnd()
DataSet1.LayoutMainRapport.Rows.Add(ligne)
LayoutMainRapportTableAdapter.Update(DataSet1)
End If

XmlSerialize directly to GZipStream throws magic number exception on Decompression

I am trying to Serialize an object to XML however my object is a generic list containing many records and causes the serializer to consume lots of memory. So I tried to serialize directly to a GZipStream with the following code:
Dim formatter As XmlSerializer = XmlSerializerFactory.GetSerializerForType(_type)
Using _ms As New MemoryStream()
Using gzStream As New GZipStream(_ms, CompressionMode.Compress, True)
_ms.Position = 0
formatter.Serialize(gzStream, obj)
_ms.Position = 0
gzStream.Flush()
gzStream.Close()
End Using
_ms.Position = 0
Dim decompressData() As Byte
Using gzStream As New GZipStream(_ms, CompressionMode.Decompress)
ReDim decompressData(9000 - 1) 'this number doesn't matter, the data in my test sample is small
Dim Len As Integer = gzStream.Read(decompressData, 0, decompressData.Length)
End Using
End Using
However I run into an InvalidDataException The magic number in GZip header is not correct. Make sure you are passing in a GZip stream. when trying to read the data into the decompressData array.
When I Serialize to a separate memory stream first and then compress that stream such as:
Dim formatter As XmlSerializer = XmlSerializerFactory.GetSerializerForType(_type)
Using _ms As New MemoryStream()
Dim uc_fileBytes() As Byte
Dim uc_len As Integer
Using _ms101 As New MemoryStream()
formatter.Serialize(_ms101, obj)
uc_fileBytes = _ms101.GetBuffer()
uc_len = _ms101.Length
End Using
Using gzStream As New GZipStream(_ms, CompressionMode.Compress, True)
_ms.Position = 0
gzStream.Write(uc_fileBytes, 0, uc_len)
gzStream.Flush()
gzStream.Close()
End Using
Dim decompressData() As Byte
Using gzStream As New GZipStream(_ms, CompressionMode.Decompress)
ReDim decompressData(9000 - 1)
Dim Len As Integer = gzStream.Read(decompressData, 0, decompressData.Length)
End Using
End Using
It works fine without error. But why does it fail when I serialize directly to the GZipStream?
The cause of the problem is because the GZipStream behaves differently (obviously) to the MemoryStream when writing to it. It doesn't handle paged writes very well.