Save binary file from SQL Server - vb.net

I'm trying to save a binary file stored in a SQL database with a SaveDialog, the function RetrieveFile retrieves the specified file from the database as a Byte array, here's what I have so far:
Private Shared Function RetrieveFile(ByVal filename As String) As Byte()
Dim connection As New SqlConnection("Data Source=SERVER\SQL2008;Initial Catalog=NorthPole;Integrated Security=True")
Dim command As New SqlCommand("SELECT pcfFile FROM Items WHERE pcfFileName=#Filename", connection)
command.Parameters.AddWithValue("#Filename", filename)
connection.Open()
Dim reader As SqlDataReader = command.ExecuteReader(System.Data.CommandBehavior.SequentialAccess)
reader.Read()
Dim memory As New MemoryStream()
Dim startIndex As Long = 0
Const ChunkSize As Integer = 256
While True
Dim buffer As Byte() = New Byte(ChunkSize - 1) {}
Dim retrievedBytes As Long = reader.GetBytes(1, startIndex, buffer, 0, ChunkSize)
memory.Write(buffer, 0, CInt(retrievedBytes))
startIndex += retrievedBytes
If retrievedBytes <> ChunkSize Then
Exit While
End If
End While
connection.Close()
Dim data As Byte() = memory.ToArray()
memory.Dispose()
Return data
End Function
Private Sub SaveAsToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveAsToolStripMenuItem.Click
Dim saveFileDialog1 As New SaveFileDialog()
saveFileDialog1.Filter = "PCF File|*.pcf|"
saveFileDialog1.Title = "Save an pcf File"
saveFileDialog1.ShowDialog()
If saveFileDialog1.FileName <> "" Then
Dim fs As System.IO.FileStream = CType(RetrieveFile("FakePCF.pcf"), System.IO.FileStream)
fs.Close()
End If
End Sub
Files are saved as "SqlDbType.VarBinary" within the database.
I get: "Index was outside the bounds of the array." on:
Dim retrievedBytes As Long = reader.GetBytes(1, startIndex, buffer, 0, ChunkSize)
The MemoryStream appears to not be retrieving the data yet the SQL sytax is correct.
What am I doing wrong?

Well, first of all, your method returns byte[] and you're trying to cast it to FileStream. You should change your handler to following:
Private Sub SaveAsToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveAsToolStripMenuItem.Click
Dim saveFileDialog1 As New SaveFileDialog()
saveFileDialog1.Filter = "PCF File|*.pcf|"
saveFileDialog1.Title = "Save an pcf File"
saveFileDialog1.ShowDialog()
If saveFileDialog1.FileName <> "" Then
Dim fs As New System.IO.FileStream (saveFileDialog1.FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write)
Dim data As Byte() = RetrieveFile("FakePCF.pcf")
fs.Write(data, 0, data.Length)
fs.Flush()
fs.Close()
End If
End Sub

Related

Incorrect progressbar value reported by the backgroundworker

I build an app, which searches for different files through the computer, including Windows, ProgramFiles, etc folders.
I've already done with the 'recursive' file search and it works now, this means all files/folders which are inaccessible will be skipped and the software will move on with the other available folders/files. But now, I have other issues...
The problem is that, for some odd reason, the application does not correctly report the progressbar value accordingly to the progress made in searching the files. This means that, the file search continues, BUT the progressbar is already 100%. The progressbar value SHOULD be 100% when the file search has been successfully completed.
====other code is already here ====
Dim int As Integer = 0
Dim filecount As Integer = 0
Private Function GetFilesRecursive(ByVal path As String) As List(Of String)
Dim lstResult As New List(Of String)
Dim stkStack As New Stack(Of String)
stkStack.Push(path)
Do While (stkStack.Count > 0)
Dim strDirectory As String = stkStack.Pop
Try
lstResult.AddRange(Directory.GetFiles(strDirectory, "*.*"))
Dim strDirectoryName As String
For Each strDirectoryName In Directory.GetDirectories(strDirectory)
stkStack.Push(strDirectoryName)
Next
Catch ex As Exception
End Try
Loop
Return lstResult
End Function
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles Quickscan.DoWork
Try
Dim i As Integer
Dim files As List(Of String) = GetFilesRecursive(FolderBrowserDialog1.SelectedPath)
For Each file As String In files
Try
Dim scanbox As New TextBox
Dim read As String = My.Computer.FileSystem.ReadAllText(System.AppDomain.CurrentDomain.BaseDirectory & "db1.db")
scanbox.Text = read.ToString
Dim md5 As MD5CryptoServiceProvider = New MD5CryptoServiceProvider
Dim f As FileStream = New FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)
f = New FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)
md5.ComputeHash(f)
Dim hash As Byte() = md5.Hash
Dim buff As StringBuilder = New StringBuilder
Dim hashByte As Byte
For Each hashByte In hash
buff.Append(String.Format("{0:X2}", hashByte))
Next
If scanbox.Text.Contains(buff.ToString) Then
AddListItem(Listbox2, "" & file)
AddListItem2(Listbox2, "" & file & "")
End If
Catch ex As Exception
End Try
If (Backgroundworker1.CancellationPending = True) Then
e.Cancel = True
Exit For
End If
SetLabelText_ThreadSafe(Me.Labelscannedfiles, file & "")
i = i + 1
SetLabelText_ThreadSafe(Me.Label2, i)
Dim pct As Integer = CInt(CDbl(i) / CDbl(files.Count) * 100)
Backgroundworker1.ReportProgress(pct)
Next file
Catch ex As UnauthorizedAccessException
End Try
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles Backgroundworker1.ProgressChanged
Label28.Text = "Computer scan is in progress... Please wait"
ProgressBar1.Value = e.ProgressPercentage / 100
Any solution or help would be highly appreciated!
In your DoWork() method...
Change:
For Each file As String In GetFilesRecursive(FolderBrowserDialog1.SelectedPath)
To:
Dim i As Integer ' <- you didn't actually declare a counter before
Dim files As List(Of String) = GetFilesRecursive(FolderBrowserDialog1.SelectedPath)
For Each file As String In files
Now you can get the count from "files" and use that in your percentage calculation:
i = i + 1 ' <-- increment our "i" counter
SetLabelText_ThreadSafe(Me.Label2, i)
Dim pct As Integer = CInt(CDbl(i) / CDbl(files.Count) * 100)
backgroundworker1.ReportProgress(pct)

How to Encrypt and Decrypt multiple text and image files within folders

Encryption and decryption for a single .txt file using the following code works:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles SelectFile.Click
'Create a File Dialog Box to select the source file
Dim dlg As New OpenFileDialog
'If OK Button is Click then add file name with path to textBox1
If dlg.ShowDialog() = DialogResult.OK Then
TextBox1.Text = dlg.FileName
End If
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Encrypt.Click
Dim outputFile As String
outputFile = "M:\Encryptions\Encrypted.txt"
Dim fsInput As New FileStream(TextBox1.Text, FileMode.Open, FileAccess.Read)
Dim fsEncrypted As New FileStream(outputFile, FileMode.Create, FileAccess.Write)
Dim sKey As String
sKey = "Helloabc"
Dim DES As New DESCryptoServiceProvider
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey)
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey)
Dim desencrypt As ICryptoTransform
desencrypt = DES.CreateEncryptor()
Dim cryptostream As New CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write)
Dim bytearrayinput(fsInput.Length) As Byte
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length)
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length)
cryptostream.Close()
fsInput.Close()
fsEncrypted.Close()
TextBox2.Text = "M:\Encryptions\Encrypted.txt"
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Decrypt.Click
Dim DES As New DESCryptoServiceProvider
Dim sKey As String
sKey = "Helloabc"
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey)
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey)
Dim fsread As New FileStream(TextBox2.Text, FileMode.Open, FileAccess.Read)
Dim desdecrypt As ICryptoTransform
desdecrypt = DES.CreateDecryptor()
Dim cryptostreamDecr As New CryptoStream(fsread, desdecrypt, CryptoStreamMode.Read)
Dim fsDecrypted As New StreamWriter("M:\Decryptions\Decrypted.txt")
fsDecrypted.Write(New StreamReader(cryptostreamDecr).ReadToEnd())
fsDecrypted.Flush()
fsDecrypted.Close()
TextBox3.Text = "M:\Decryptions\Decrypted.txt"
End Sub
However when attempting to encrypt/decrypt multiple files, the original output encryption/decryption file ("Encrypted.txt") will be replaced by the new output.
How do you create a loop in order to create multiple encrypted files with different file names?
Use a counter: 0, 1, 2, 3, 4 ... Append the counter to the filename and step the counter after each file is written:
Encrypted00.dat
Encrypted01.dat
Encrypted02.dat
Encrypted03.dat
...
It is probably a mistake to call the encrypted files .txt since they will not be text. They are binary data. If you want them as text then you will need to convert to Base64 format, and convert back to binary data before decrypting.
Alternatively, zip all the files in the directory into a single file and encrypt that single file.

File locked after encryption

I have a small app that is designed to go through a folder and encrypt all the files and delete the non-encrypted version. The encryption works correctly but when I go to delete the file it is locked. Once the app is closed the file is no longer locked. My code is
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim path As String = Directory.GetCurrentDirectory()
Dim parent As DirectoryInfo = Directory.GetParent(path)
Dim completions As String = parent.FullName & "\Attachments_Completions"
Dim di As New DirectoryInfo(completions)
Dim fi As FileInfo() = di.GetFiles()
Dim dra As FileInfo
For Each dra In fi
If dra.Name <> "Completions.txt" And dra.Name <> "Attachments.txt" Then
Dim tmpFileName As String = String.Format("{0}\qqzr_{1}", dra.DirectoryName, dra.Name)
Dim encryptedName As String = String.Format("{0}\{1}", dra.DirectoryName, dra.Name)
FileSystem.Rename(String.Format("{0}\{1}", dra.DirectoryName, dra.Name), String.Format("{0}\qqzr_{1}", dra.DirectoryName, dra.Name))
cryptFile(tmpFileName, encryptedName, False)
File.Delete(tmpFileName)
End if
Next
End Sub
Sub cryptFile(ByVal sInputFilename As String, ByVal sOutputFilename As String, ByVal switch As Boolean)
' * Used to Encrypt or Decrypt a file
' ***********************************
Dim sKey As String = "a2#R|+~"
Try
Dim DES As New DESCryptoServiceProvider()
DES.Key() = ASCIIEncoding.ASCII.GetBytes(sKey)
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey)
'Read the input file into array
Dim fsInput As New FileStream(sInputFilename, FileMode.Open, FileAccess.Read)
Dim bytearrayinput(fsInput.Length - 1) As Byte
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length)
'Define the crypto transformer
Dim cryptoTransform As ICryptoTransform
If switch Then
cryptoTransform = DES.CreateEncryptor()
Else
cryptoTransform = DES.CreateDecryptor
End If
'Create the encrypting streams
Dim fsEncrypted As New FileStream(sOutputFilename, FileMode.Create, FileAccess.Write)
Dim cryptostream As New CryptoStream(fsEncrypted, cryptoTransform, CryptoStreamMode.Write)
'Write the output file
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length)
cryptostream.Close()
cryptostream.Dispose()
'Release the input file
fsInput.Close()
fsInput.Dispose()
'Release the output file
fsEncrypted.Close()
fsEncrypted.Dispose()
Catch ex As Exception
End Try
End Sub
End Class
Can anyone help?
Cheers
James
Just add this
Catch ex As Exception
Debug.Write(ex.Message)
End Try
...and see what happens...
Thanks for the responses.
I have managed to resolve the problem by adding File.SetAttributes(tmpFileName, FileAttributes.Normal) before the File.Delete command

Loading image from database to picturebox

I'm new to visual basic and I have a problem in loading the image from my database. I'm currently using image data type. This is how I save the image
Imports System.Data
Imports System.Data.SqlClient
Public Class ScannerX_Add
Dim ImageFilename As String
Dim ImageUpload As Image
Private Sub ButtonX1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Btn_Scan.Click
Try
OpenFileDialog1.Title = "Please Select a File"
OpenFileDialog1.InitialDirectory = "C:\Users\ITTestServer\Desktop\Dekstop\"
OpenFileDialog1.ShowDialog()
ScannerX_Pic.Image = Image.FromFile(OpenFileDialog1.FileName)
ImageFilename = OpenFileDialog1.FileName
ImageUpload = Image.FromFile(OpenFileDialog1.FileName)
Catch ex As Exception
MsgBox("Please insert scan finger")
Exit Sub
End Try
End Sub
Private Sub Btn_Save_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Btn_Save.Click
If ImageFilename <> "" Then
Dim imageNameTemp As String
imageNameTemp = ImageFilename
While (imageNameTemp.Contains("\"))
imageNameTemp = imageNameTemp.Remove(0, imageNameTemp.IndexOf("\") + 1)
End While
Dim ms As New IO.MemoryStream
If ImageFilename.Contains("jpeg") Or ImageFilename.Contains("jpg") Then
ImageUpload.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
End If
If ImageFilename.Contains("png") Then
ImageUpload.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
End If
If ImageFilename.Contains("gif") Then
ImageUpload.Save(ms, System.Drawing.Imaging.ImageFormat.Gif)
End If
If ImageFilename.Contains("bmp") Then
ImageUpload.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp)
End If
Dim b() As Byte = ms.ToArray()
Dim cmd As New SqlCommand("Insert into Scanner (Name,Finger) VALUES('" & TxtBox_Name.Text.Trim & "','" & imageNameTemp & "')", Connection)
cmd.Parameters.Add("#BLOBData", SqlDbType.Image, b.Length).Value = b
cmd.ExecuteNonQuery()
MsgBox("Profile Has Been Saved")
Me.Close()
End If
End Sub
Now I need to load my image to picturebox which is currently in the Main form.
Private Sub ButtonX1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonX1.Click
Command.CommandText = ("select Finger FROM Scanner")
Command.Connection = Connection
Dim da As New SqlDataAdapter(Command)
Dim ds As New DataSet()
da.Fill(ds, "projectimages")
Dim c As Integer = ds.Tables(0).Rows.Count
If c > 0 Then
Dim bytBLOBData() As Byte = _
ds.Tables(0).Rows(c - 1)("imagedate")
Dim stmBLOBData As New MemoryStream(bytBLOBData)
ScannerX_Pic2.Image = Image.FromStream(stmBLOBData)
End If
End Sub
Now I get the error Object reference not set to an instance of an object.
To save an image you could do something like this
Dim sql As String = "INSERT INTO Information VALUES(#name,#photo)"
Image Img = PictureBox1.BackgroundImage
Dim cmd As New SqlCommand(sql, con)
cmd.Parameters.AddWithValue("#name", TextBox1.Text)
Dim ms As New MemoryStream()
Img.Save(ms, Img.RawFormat)
Dim data As Byte() = ms.GetBuffer()
Dim p As New SqlParameter("#photo", SqlDbType.Image)
p.Value = data
cmd.Parameters.Add(p)
cmd.ExecuteNonQuery()
And to retrieve an image
cmd = New SqlCommand("select photo from Information where name='Rose'", con)
Dim imageData As Byte() = DirectCast(cmd.ExecuteScalar(), Byte())
If Not imageData Is Nothing Then
Using ms As New MemoryStream(imageData, 0, imageData.Length)
ms.Write(imageData, 0, imageData.Length)
PictureBox1.BackgroundImage = Image.FromStream(ms, True)
End Using
Also check out this article it's doing exactly what you are trying to achieve
http://www.codeproject.com/Articles/437937/Save-and-Retrieve-Image-from-a-SQL-Server-Database
The sql is selecting a column named "Finger":
"select Finger FROM Scanner"
But it's later trying to build an Image object from a column named "imagedate":
ds.Tables(0).Rows(c - 1)("imagedate")
It needs to use the same column name as the SQL result set:
ds.Tables(0).Rows(c - 1)("Finger")
There may be other problems as well, but that's what jumped out at me right away.

Save/open OLE object from .mdb in vb winform

I am trying to add a feature to my vb winform program where a user can attach files (.doc, .docx, .jpg, .pdf) to a mdb file that holds other text data. The binary file and the file name are stored in the DB. The file name with path is passed as variable 'fpath'. below is what I have thus far (It is now working, meaning it saves the file name and binary data). Now, how can the user open the saved file? And, if it is a .doc or PDF etc, how do I make the default associated program open it? . Can someone help me with the rest?
Here is the code to store the OLE object:
Private Sub btnClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClear.Click
'data connection
Dim cn As New OleDb.OleDbConnection
cn.ConnectionString = "Provider=Microsoft.Jet.OleDb.4.0; Data Source=" & _
Application.StartupPath & "\data.mdb"
cn.Open()
'file name without path
Dim flName As String = filename.Text
'open file from the disk (file path is the path to the file to be opened)
Using fileStream As FileStream = File.OpenRead(fpath)
'create new MemoryStream object
Dim memStream As New MemoryStream()
memStream.SetLength(fileStream.Length)
'read file to MemoryStream
fileStream.Read(memStream.GetBuffer(), 0, CInt(Fix(fileStream.Length)))
Dim strImage As String = "?"
Dim arr As Byte()
arr = memStream.GetBuffer
Dim cmd As New OleDb.OleDbCommand
cmd.Connection = cn
cmd.CommandText = "INSERT INTO tblstudent(name, photo) VALUES( ?, ?)"
cmd.Parameters.Add("#name", OleDbType.Char).Value = flName
cmd.Parameters.Add("#photo", OleDb.OleDbType.Binary).Value = arr
cmd.ExecuteNonQuery()
MsgBox("Data save successfully!")
cn.Close()
End Using
End Sub
After two weeks of searching and trying a bunch of methods, I got it to work. The code above is corrected and working for uploading a file to a .mdb file. The code below will retrieve it. Yes, I know saving files to a mdb is not the best, but there will only be a couple docs or pdf, and I need it all in one file for easy sharing between users.
Private Builder As New OleDbConnectionStringBuilder With _
{ _
.DataSource = IO.Path.Combine(Application.StartupPath & "\data.mdb"), _
.Provider = "Microsoft.Jet.OleDb.4.0" _
}
Private Sub btnGetfile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGetfile.Click
Dim selfile As String = fileDgv.CurrentCell.Value.ToString
Dim cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString}
Dim cmd As New OleDbCommand With _
{ _
.Connection = cn, _
.CommandText = "SELECT photo FROM tblstudent WHERE name='" & selfile & "'" _
}
Dim NoDataList As New List(Of String)
Dim dr As OleDbDataReader = Nothing
Dim FileStream As System.IO.FileStream
Dim Reader As OleDbDataReader
Dim Data() As Byte = Nothing
Dim Writer As System.IO.BinaryWriter = Nothing
Dim bufferSize As Integer = 1000
Dim buffer(bufferSize - 1) As Byte
Dim startIndex As Long = 0
Dim numberOfBytes As Long = 0
cn.Open()
Reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)
Reader.Read()
FileStream = New System.IO.FileStream(
IO.Path.Combine("C:\temp3", "temp", selfile),
System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write)
Writer = New System.IO.BinaryWriter(FileStream)
Do
numberOfBytes = Reader.GetBytes(0, startIndex, buffer, 0, bufferSize)
If numberOfBytes = 0 Then
Exit Do
End If
Writer.Write(buffer, 0, CInt(Fix(numberOfBytes)))
startIndex += numberOfBytes
Loop While True
Writer.Flush()
If Writer IsNot Nothing Then
Writer.Close()
End If
If FileStream IsNot Nothing Then
FileStream.Close()
End If
If Reader IsNot Nothing Then
Reader.Close()
End If
cn.Close()
MessageBox.Show("Done")
End Sub