我正在尝试从我的VB.Net Winforms应用程序上传文件(可能非常大)到SQL Server 2008(C#答案也可以接受)。
使用SQL Server的FILESTREAM数据类型将文件存储为Varbinary(MAX)。
我通过将FileStream作为SqlParameter传递给服务器。
这没关系。但是,由于大文件需要一段时间才能上传,我想在UI上向ProgressBar报告进度。
我很确定我需要使用Async / Await。主要问题实际上是获得进步价值。由于我没有对FileStream做任何事情,只是将它作为SqlParameter传递,我不知道如何获得进度值。我怎样才能做到这一点?
我考虑过将流复制到另一个并获取进度值,但我认为这意味着将整个文件读入内存,我甚至不确定它是否可行。
是否有FileStream的Async方法可以满足我的需求?或者我有更好的方式来做这件事吗?
感谢。
答案 0 :(得分:0)
只是为了让你知道我做了什么来解决这个问题......
我很清楚这种解决方案远非理想。必须有一个更好的方法,我仍然希望改进它...但目前,它似乎做我需要的。我们将非常感激地收到任何意见或建议。
请参阅下面我简化的完整评论代码:
//Create the FileStream
Using SourceStream As New FileStream("PathToTheFile", FileMode.Open)
//Set the max value of your ProgressBar to the length of the stream
ProgressPb.Maximum = SourceStream.Length
//Start the task of sending the file to the DB (saving a reference to the task for later use.
Dim FileUpload As Task(Of Boolean) = Task.Run(Function() SendToDB())
//declare a variable to hold the last known position in the stream
Dim LastPosition As Long = 0
//loop until we we are done (until the current position is at the end of the stream)
Do
//only do something if the position in the strang has changed since we last checked.
If SourceStream.Position <> LastPosition Then
//save the current position for next time
LastPosition = SourceStream.Position
//set the value of your progress bar to the current position in the stream
ProgressPb.Value = SourceStream.Position
//set your status label text as you wish
StatusLbl.Text = "Uploading: " & Math.Round(SourceStream.Position / 1048576) & "MB of " & Math.Round(SourceStream.Length / 1048576) & "MB"
//call do events to save locking up your UI
Application.DoEvents()
End If
//limit the checks (8 times per second seems reasonably responsive)
Threading.Thread.Sleep(1000 / 8)
Loop Until LastPosition = SourceStream.Position
//set your status label text as "Finalising" or similar (there is a short delay of 1-2 seconds after we reach the end of the stream but before we recieve a response back from the DB).
StatusLbl.Text = "Uploading: " & Math.Round(SourceStream.Position / 1048576) & "MB of " & Math.Round(SourceStream.Length / 1048576) & "MB Complete. Finalising..."
//wait for the response from the database
Res = Await FileUpload
//set your status label text "Complete" or similar
StatusLbl.Text = "Uploading: " & Math.Round(SourceStream.Position / 1048576) & "MB of " & Math.Round(SourceStream.Length / 1048576) & "MB Complete. Finalising... Complete!"
//check the result from the database and do stuff accordingly.
If Res Then
MsgBox("Success")
Else
MsgBox("Failed")
End If
End Using
修改强>
只是一个更新。我已将代码重构为&#34; StreamWatcher&#34;类。请参阅下面的课程代码:
Imports System.IO
Public Class StreamWatcher
Private Const MaxProgressEventsPerSecond As Integer = 200
Private Property _Stream As Stream
Private Property _PreviousPosision As Long
Public ReadOnly Property StreamPosition As Long
Get
If IsNothing(_Stream) Then
Return 0
Else
Return _Stream.Position
End If
End Get
End Property
Public ReadOnly Property StreamLength As Long
Get
If IsNothing(_Stream) Then
Return 0
Else
Return _Stream.Length
End If
End Get
End Property
Private Property _TimeStarted As DateTime? = Nothing
Private Property _TimeFinished As DateTime? = Nothing
Public ReadOnly Property SecondsTaken As Double
Get
If IsNothing(_TimeStarted) Then
Return 0.0
Else
If IsNothing(_TimeFinished) Then
Return (DateTime.Now - _TimeStarted.Value).TotalSeconds
Else
Return (_TimeFinished.Value - _TimeStarted.Value).TotalSeconds
End If
End If
End Get
End Property
Private Property _UpdatesCalled As Integer = 0
Public ReadOnly Property Updates As Integer
Get
Return _UpdatesCalled
End Get
End Property
Private Property _Progress As IProgress(Of EventType)
Private Enum EventType
Progressed
Completed
End Enum
Public Event Progressed()
Public Event Completed()
Public Sub Watch(ByRef StreamToWatch As Stream)
Reset()
_Stream = StreamToWatch
Dim ProgressHandler As New Progress(Of EventType)(Sub(Value) EventRaiser(Value))
_Progress = TryCast(ProgressHandler, IProgress(Of EventType))
_TimeStarted = DateTime.Now
Task.Run(Sub() MonitorStream())
End Sub
Private Sub MonitorStream()
Do
If _PreviousPosision <> StreamPosition Then
_PreviousPosision = StreamPosition
_Progress.Report(EventType.Progressed)
//limit events to max events per second
Task.Delay(1000 / MaxProgressEventsPerSecond).Wait()
End If
Loop Until (Not IsNothing(_Stream)) AndAlso (StreamPosition = StreamLength)
_TimeFinished = DateTime.Now
_Progress.Report(EventType.Completed)
End Sub
Private Sub EventRaiser(ByVal EventToRaise As EventType)
Select Case EventToRaise
Case EventType.Progressed
_UpdatesCalled += 1
RaiseEvent Progressed()
Case EventType.Completed
_UpdatesCalled += 1
RaiseEvent Completed()
End Select
End Sub
Private Sub Reset()
_Stream = Nothing
_PreviousPosision = 0
_TimeStarted = Nothing
_TimeFinished = Nothing
_UpdatesCalled = 0
End Sub
End Class
用法如下:
Private WithEvents StreamWatcher As New StreamWatcher
Private Async Sub SaveBtn_Click(sender As Object, e As EventArgs) Handles SaveBtn.Click
Dim Res As Boolean
Using FS As New FileStream("MyFilePath", FileMode.Open)
//this attaches the the watcher to the stream
StreamWatcher.Watch(FS)
ProgressBar.Maximum = FS.Length
//This waits for the function to finish before continuing, without locking up the UI... be careful not to dispose the stream before it's finished.
Res = Await Task.Run(Function() MySendToDBFunction(MyParam1))
StatusLbl.Text += " Complete!"
End Using
If Res Then
MessageBox.Show("Success")
Else
MessageBox.Show("Fail")
End If
End Sub
//Called whenever the position in the stream changes (upto the max calls per second as defined in the StreamWatcher class - 200 seems to be more than enough))
Private Sub FSWithProgeress_Progressed() Handles StreamWatcher.Progressed
ProgressBar.Value = StreamWatcher.StreamPosition
StatusLbl.Text = "Uploading: " & Math.Round(StreamWatcher.StreamPosition / 1048576) & "MB of " & Math.Round(StreamWatcher.StreamLength / 1048576) & "MB"
End Sub
//called once the end of the stream is reached
Private Sub FSWithProgeress_Completed() Handles StreamWatcher.Completed
StatusLbl.Text = "Upload: Complete. Finalising..."
End Sub