Saving Image into memory stream Error - vb.net

I'm trying to:
1) Scan an image and insert it to a memorystream.
2) Show it in picturebox1.
3) Read picturebox1 image into memorystream and save it to oledb.
here's my code :
1st) Scanning by WIA Dialog and inserting image to memorystream then showing it in picturebox1:
Try
Dim CD As New WIA.CommonDialog
Dim F As WIA.ImageFile = CD.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType)
If F IsNot Nothing Then
Dim MStream As IO.MemoryStream = Nothing
Try
'Convert the raw scanner output into a byte array
Dim ImageBytes() As Byte = DirectCast(F.FileData.BinaryData, Byte())
'Read the image data bytes into a MemoryStream
MStream = New IO.MemoryStream(ImageBytes)
PictureBox1.Image = System.Drawing.Image.FromStream(MStream)
Catch ex As Exception
MsgBox("An error occurred: " & ex.Message, MsgBoxStyle.Critical)
End Try
If MStream IsNot Nothing Then MStream.Dispose()
End If
Catch ex As Exception
MsgBox(ex.message, MsgBoxStyle.Critical)
End Try
This is working perfectly.
The problem is coming,
2nd) Reading image from picturebox1 into new memorystream then save it to oledb:
If conn.State = ConnectionState.Closed Then
conn.ConnectionString = connstring
conn.Open()
'Reading image from picturebox1 and then insert it to a new memorystream.
Dim arrImage() As Byte
Dim myMs As New IO.MemoryStream
PictureBox1.Image.Save(myMs, System.Drawing.Imaging.ImageFormat.Jpeg)
arrImage = myMs.GetBuffer
'Now save image to oledb from the memorystream.
Dim query As String = "INSERT INTO Guestinfo ([IDImage]) VALUES (#IDImage)"
Dim command As New OleDbCommand
With command
.CommandText = query
.Parameters.Add("#IDImage", OleDb.OleDbType.Binary).Value = arrImage
.Connection = conn
.ExecuteNonQuery()
End With
MsgBox("Saved Successfully!", MsgBoxStyle.Information)
conn.Close()
ListView1.Items.Clear()
loadlistview()
Me.Close()
End If
Error is on this line :
PictureBox1.Image.Save(myMs, System.Drawing.Imaging.ImageFormat.Jpeg)
I might see that this error because during the scanning process we were converting the image into byte array to put it into memorystream, but while reading process we were trying to read the picturebox image as Jpeg into memorystream.
I can see it's complicated for me as a beginner in visualbasic any help would be appreciated.

Per the documentation for Image.FromStream, the stream must be kept open for the lifetime of the Image, but you are disposing of the stream in this line:
If MStream IsNot Nothing Then MStream.Dispose()
Try removing that line from your first code block.
I suppose the MemoryStream should be disposed of at some point, but perhaps the PictureBox and Image classes will take care of that for you. Otherwise, you may have to add a Dispose call when the form is closed.

Related

Skipping a line of code if an image isn't selected

If a user selects an image it will save the image as per the below code:
PictureBox1.Image.Save(PicFile, System.Drawing.Imaging.ImageFormat.Jpeg)
Now if the user decides not to select an image, is there a way to skip this code? I currently get the following error:
Additional information: Object reference not set to an instance of an object.
This is what I've tried:
Dim opf As New OpenFileDialog
opf.Filter = "Choose Image(.jpg;.png;*.gif)|*.jpg;*.png;*.gif"
If opf.ShowDialog = DialogResult.OK Then
PictureBox1.Image = Image.FromFile(opf.FileName)
End If
Try
Dim ms As New MemoryStream
PictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
Dim img As Byte()
img = ms.ToArray()
DataGridView1.Rows.Add(img)
Catch ex As Exception
MessageBox.Show(ex.Message.ToString())
End Try
Now if the user decides not to select an image, is there a way to skip this code?
If by this you mean the user selects Cancel, then yes. Simply place the Try[...]Catch block into your If statement and it will only attempt the code if the user selects Open.
Something like this:
Dim opf As New OpenFileDialog
opf.Filter = "Choose Image(.jpg;.png;*.gif)|*.jpg;*.png;*.gif"
If opf.ShowDialog = DialogResult.OK Then
PictureBox1.Image = Image.FromFile(opf.FileName)
Try
Dim ms As New MemoryStream
PictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
Dim img As Byte()
img = ms.ToArray()
DataGridView1.Rows.Add(img)
Catch ex As Exception
MessageBox.Show(ex.Message.ToString())
End Try
End If
If you only wanted to skip certain bits of code you could do a check on opf.FileName. As an example:
If opf.FileName <> "" Then
PictureBox1.Image = Image.FromFile(opf.FileName)
End If

SqlFileStream writing in separate thread not working

Learning and testing using Sql FILESTREAM for a web app. A client uploads a large file form the web page which takes 'X' time and when fully uploaded shows 100% complete. However, very large files also take time for SqlFileStream to write to the file system so I want to spin off a thread to complete that part. The code I've got seems to work fine but no data ends up in the filestream file.
I'm wrapping the initial record creation in it's own transaction scope and using a separate transaction scope in the thread. In the threaded routine I have the appropriate PathName() and TransactionContext but I assume I'm missing something while using a thread.
I've commented out the normal SqlFileStream call which works fine. Can you see anything wrong with what I'm doing here?
Public Function StoreFileStream()
Dim json As New Dictionary(Of String, Object)
Dim parms As New FileStreamThreadParameters
If HttpContext.Current.Request.Files.Count > 0 Then
Dim file As HttpPostedFile = HttpContext.Current.Request.Files(0)
If "contentType" <> String.Empty Then
Dim fs As Stream = file.InputStream
Dim br As New BinaryReader(fs)
Dim noBytes As New Byte()
Try
Dim filePath As String = ""
Dim trxContext As Byte() = {}
Dim baseFileId As Integer
Using trxScope As New TransactionScope
Using dbConn As New SqlConnection(DigsConnStr)
Using dbCmd As New SqlCommand("ADD_FileStreamFile", dbConn)
dbConn.Open()
Using dbRdr As SqlDataReader = dbCmd.ExecuteReader(CommandBehavior.SingleRow)
dbRdr.Read()
If dbRdr.HasRows Then
filePath = dbRdr("Path")
trxContext = dbRdr("TrxContext")
baseFileId = dbRdr("BaseFileID")
End If
dbRdr.Close()
End Using
' Code below writes to file, but trying to offload this to a separate thread so user is not waiting
'Using dest As New SqlFileStream(filePath, trxContext, FileAccess.Write)
' fs.CopyTo(dest, 4096)
' dest.Close()
'End Using
End Using
dbConn.Close()
End Using
trxScope.Complete()
End Using ' transaction commits here, not in line above
parms.baseFileId = baseFileId
parms.fs = New MemoryStream
fs.CopyTo(parms.fs)
Dim fileUpdateThread As New Threading.Thread(Sub()
UpdateFileStreamThreaded(parms)
End Sub)
fileUpdateThread.Start()
json.Add("status", "success")
Catch ex As Exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex)
json.Add("status", "failure")
json.Add("msg", ex.Message)
json.Add("procedure", System.Reflection.MethodBase.GetCurrentMethod.Name)
End Try
Else
json.Add("status", "failure")
json.Add("msg", "Invalid file type")
json.Add("procedure", System.Reflection.MethodBase.GetCurrentMethod.Name)
End If
End If
Return json
End Function
Public Class FileStreamThreadParameters
Public Property baseFileId As Integer
Public fs As Stream
End Class
Private Sub UpdateFileStreamThreaded(parms As FileStreamThreadParameters)
Dim filePath As String = ""
Dim trxContext As Byte() = {}
Try
Using trxScope As New TransactionScope
Using dbConn As New SqlConnection(DigsConnStr)
Using dbCmd As New SqlCommand("SELECT FileBytes.PathName() 'Path', GET_FILESTREAM_TRANSACTION_CONTEXT() 'TrxContext' FROM FileStreamFile WHERE Id = " & parms.baseFileId, dbConn)
dbConn.Open()
Using dbRdr As SqlDataReader = dbCmd.ExecuteReader(CommandBehavior.SingleRow)
dbRdr.Read()
If dbRdr.HasRows Then
filePath = dbRdr("Path")
trxContext = dbRdr("TrxContext")
End If
dbRdr.Close()
Using dest As New SqlFileStream(filePath, trxContext, FileAccess.Write)
parms.fs.CopyTo(dest, 4096)
dest.Close()
End Using
End Using
End Using
dbConn.Close()
End Using
trxScope.Complete()
End Using
Catch ex As Exception
'Elmah.ErrorSignal.FromCurrentContext().Raise(ex)
End Try
End Sub
Obviously this is a complex issue. I actually got it to work with the code below. However I eventually abandoned using SQL FILESTREAM altogether due to too much complexity in getting it all set up.
This is an existing web application with the sql server on a different box. After I got the filestreaming to work the next hurdle was authentication setup. Filestream requires Integrated Security on your connection string. Even with windows authentication on our Intranet app, I could not get the web app to use the windows credentials when connecting to the database. It always seemed to use the computer name or the app pool service. I tried many many examples I found on the net and here to no avail. Even if I got that to work then I would want to use and Active Directory group over individual logins which looked to be another hurdle.
This app stores documents in a varbinary column so that full text search can be enabled at some point. The issue was with large files which are generally pictures or videos. Since you can't search text on those anyway the strategy now is to store those on the file system and all other searchable docs (.docx, .pptx, etc) will still be stored in the varbinary. I'm actually sad that I could not get filestream to work as it seems like the ideal solution. I'll come back to it some day but it really should not be so frickin complicated. :-(
The code I got working is:
Dim filePath As String = ""
Dim trxContext As Byte() = {}
Dim baseFileId As Integer
Using trxScope As New TransactionScope
Using dbConn As New SqlConnection(DigsFSConnStr)
Using dbCmd As New SqlCommand("NEW_FileStreamBaseFile", dbConn)
dbCmd.CommandType = CommandType.StoredProcedure
dbCmd.Parameters.AddWithValue("#Title", fileDesc)
dbCmd.Parameters.AddWithValue("#Summary", summary)
dbCmd.Parameters.AddWithValue("#Comments", comments)
dbCmd.Parameters.AddWithValue("#FileName", uploadedFileName)
dbCmd.Parameters.AddWithValue("#ContentType", contentType)
dbCmd.Parameters.AddWithValue("#FileExt", ext)
'dbCmd.Parameters.AddWithValue("#FileBytes", noBytes) ' now that were offloading the file byte storage to a thread
dbCmd.Parameters.AddWithValue("#UploadedByResourceID", uploadedByResourceID)
dbCmd.Parameters.AddWithValue("#UploadedByShortName", uploadedByShortName)
dbCmd.Parameters.AddWithValue("#FileAuthor", fileAuthor)
dbCmd.Parameters.AddWithValue("#TagRecID", tagRecID)
dbCmd.Parameters.AddWithValue("#UserID", samAccountName)
dbCmd.Parameters.AddWithValue("#FileDate", fileDate)
dbCmd.Parameters.AddWithValue("#FileType", fileType)
dbCmd.Parameters.AddWithValue("#FileTypeRecID", fileTypeRecId)
' Save to file system too for xod conversion
file.SaveAs(HttpContext.Current.Server.MapPath("~/files/uploaded/") & uploadedFileName)
dbConn.Open()
Using dbRdr As SqlDataReader = dbCmd.ExecuteReader(CommandBehavior.SingleRow)
dbRdr.Read()
If dbRdr.HasRows Then
filePath = dbRdr("Path")
trxContext = dbRdr("TrxContext")
json.Add("baseFileId", dbRdr("BaseFileID"))
virtualFileRecId = dbRdr("VirtualFileRecID")
dbStatus = dbRdr("status")
If dbStatus = "failure" Then
json.Add("msg", dbRdr("msg"))
End If
End If
dbRdr.Close()
End Using
' Prepare and start Task thread to write the file
If dbStatus = "success" Then
bytes = br.ReadBytes(fs.Length)
Dim task As New System.Threading.Tasks.Task(
Sub()
UpdateNewFileStreamBytes(virtualFileRecId, bytes)
End Sub)
task.Start()
json.Add("status", "success")
Else
json.Add("status", "failure")
End If
End Using
dbConn.Close()
End Using
trxScope.Complete()
End Using ' transaction commits here, not in line above
With the task procedure of:
Private Sub UpdateNewFileStreamBytes(virtualFileRecId As Integer, fileBytes As Byte())
Dim filePath As String = ""
Dim trxContext As Byte() = {}
Try
Using trxScope As New TransactionScope
Using dbConn As New SqlConnection(DigsFSConnStr)
Using dbCmd As New SqlCommand("UPD_FileStreamBaseFile", dbConn)
dbCmd.CommandType = CommandType.StoredProcedure
dbCmd.Parameters.AddWithValue("#VirtualFileRecID", virtualFileRecId)
dbConn.Open()
Using dbRdr As SqlDataReader = dbCmd.ExecuteReader(CommandBehavior.SingleRow)
dbRdr.Read()
If dbRdr.HasRows Then
filePath = dbRdr("Path")
trxContext = dbRdr("TrxContext")
End If
dbRdr.Close()
Using dest As New SqlFileStream(filePath, trxContext, FileAccess.Write)
dest.Write(fileBytes, 0, fileBytes.Length)
dest.Close()
End Using
End Using
End Using
dbConn.Close()
End Using
trxScope.Complete()
End Using
Catch ex As Exception
Elmah.ErrorSignal.FromCurrentContext().Raise(ex)
End Try

Trouble in saving a longblob image to database

I want to get the image from the datagridview then save it to the database. Can anyone help me with this? It will be so much appreciated
Below is my code in saving the image in the database. I don't know why but it doesn't actually save anything.
Try
connectionSync()
Dim a, b As String
Dim Sql = "INSERT INTO SAMPLE (ID, IMG)values(#a,#b)"
For i As Integer = 0 To Me.DataGridView1.Rows.Count - 1
a = Me.DataGridView1.Rows(i).Cells(0).Value.ToString()
Dim cmd As New MySqlCommand(Sql, ConSync)
Dim memorystream1 As New MemoryStream()
Dim filename As String = DataGridView1.Rows(i).Cells(1).Value
Dim bitmaps As New Bitmap(filename)
bitmaps.Save(memorystream1, Imaging.ImageFormat.Jpeg)
Dim pic() As Byte = memorystream1.GetBuffer()
cmd.Parameters.AddWithValue("#a", a)
cmd.Parameters.AddWithValue("#b", bitmaps)
cmd.ExecuteNonQuery()
cmd.Parameters.Clear()
Next
ConSync.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try

Trouble in converting an image from database to byte and back to image

I have a button and a datagridview in my form. I need to get the image from the database and display its value in the datagrid then get the value of the image in the datagrid to copy it to another database.
Specifically, I'm copying the image (blob) from table1 of database1 to table 1 of database2.
Button1_click:
Dim img As Image
Dim bArr As Byte()
Try
Dim Sql = "Select ID, IMG from sample"
connectionOnline()
Dim cmd = New MySqlCommand(Sql, ConOnline)
Dim dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
DataGridView1.Rows.Clear()
While dr.Read = True
img = dr(1)
bArr = imgToByteArray(img)
DataGridView1.Rows.Add(dr(0), bArr)
End While
ConOnline.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
Try
connectionSync()
Dim a, b As String
Dim Sql = "INSERT INTO SAMPLE (ID, IMG)values(#a,#b)"
For i As Integer = 0 To Me.DataGridView1.Rows.Count - 1
a = Me.DataGridView1.Rows(i).Cells(0).Value.ToString()
Dim img1 As Image = byteArrayToImage(bArr)
Dim cmd As New MySqlCommand(Sql, ConSync)
cmd.Parameters.AddWithValue("#a", a)
cmd.Parameters.AddWithValue("#b", img1)
cmd.ExecuteNonQuery()
cmd.Parameters.Clear()
Next
ConSync.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
Try
connectionSync()
Dim Sql = "INSERT INTO B.SAMPLE(ID, IMG) SELECT ID, IMG FROM C.SAMPLE WHERE not exists (SELECT 1 from B.SAMPLE WHERE B.SAMPLE.ID=C.SAMPLE.ID)"
Dim cmd = New MySqlCommand(Sql, ConSync)
With cmd
.ExecuteNonQuery()
End With
MsgBox("Success", vbInformation, "Save")
ConSync.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
Try
connectionOffline()
Dim Sql = "UPDATE SAMPLE SET IMG=(SELECT C.SAMPLE.NAME FROM C.SAMPLE WHERE C.SAMPLE.ID=B.SAMPLE.ID) WHERE B.SAMPLE.ID=(SELECT C.SAMPLE.ID FROM C.SAMPLE WHERE C.SAMPLE.ID=B.SAMPLE.ID)"
Dim cmd = New MySqlCommand(Sql, ConOffline)
With cmd
.ExecuteNonQuery()
End With
MsgBox("Success")
ConOffline.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
B and C are databases
While sample is the table
below are the functions i used to convert the image
Public Function imgToByteArray(ByVal img As Image) As Byte()
Using mStream As New MemoryStream()
img.Save(mStream, img.RawFormat)
Return mStream.ToArray()
End Using
End Function
Public Function byteArrayToImage(ByVal byteArrayIn As Byte()) As Image
Using mStream As New MemoryStream(byteArrayIn)
Return Image.FromStream(mStream)
End Using
End Function
The result (in the database; IMG) only shows: "System.Drawing.Bitmap" instead of the actual image
You are storing only a "Image.ToString".
Try to use a Binary parameter:
Dim yourParam As new SqlParameter("#b", System.Data.SqlDbType.Binary)
yourParam.Direction = ParameterDirection.Input
' yourParam.value= yourImage ' you can't place a system.drawin.image here
Dim ms As New MemoryStream()
yourImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
yourParam.value= ms.ToArray() ' similar to your imgToByteArray function ....
cmd.Parameters.Add(yourParam)

VB.NET - "Cannot access a closed file" when trying to read contents of a file

Here is my code for the form...
I am attempting to read the contents of files to be able to store in a DB as blob
Try
Dim fs = New FileStream(TextBox1.Text, FileMode.Open, FileAccess.Read)
Dim rawData = New Byte(fs.Length) {}
fs.Read(rawData, 0, fs.Length)
fs.Close()
' Get File size
Dim filesize As Long = fs.Length
' Get Extension
Dim extension As String = Path.GetExtension(TextBox1.Text)
'SQL query
Dim cmdText = "INSERT INTO files VALUES (file_id, #fname, #content, #size, #ext, #comments, #vers, #auth);"
'commit SQL query with connection statement
Dim com As New MySqlCommand(cmdText, con)
'place fields into parameters for the query
com.Parameters.AddWithValue("#fname", filename.Text)
com.Parameters.AddWithValue("#content", rawData)
com.Parameters.AddWithValue("#size", filesize)
com.Parameters.AddWithValue("#ext", extension)
com.Parameters.AddWithValue("#comments", comments.Text)
com.Parameters.AddWithValue("#vers", version.Text)
com.Parameters.AddWithValue("#auth", home.userid.Text)
'this will commit the record to the DB
con.Close()
con.Open()
com.ExecuteNonQuery()
con.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
As seen in the link below, Length property can throw IOException if the file is closed or closing
http://msdn.microsoft.com/en-us/library/system.io.filestream.length.aspx
You are accessing the length after closing the file. just flip them as shown below
' Get File size
Dim filesize As Long = fs.Length
fs.Close()