I am trying to develop an application that will fetch a file from a web server using a generated Url path.
A new file on the web server is created every second, and I am trying to access that file and display it in a PictureBox (old files are not deleted).
I have ran into some problems, the server is returning a 404
error, but I can't figure out why.
The application seems to be unable to download the file using the generated image source Url, but when I visit the generated link in a web browser (e.g Chrome, Internet Explorer), it works just fine.
I also ran into some problems along the way of having the URL not formatted correctly.
Try 1:
I tried to download the file using a string outputted by my URl generator. The URl must be in a date form: yyyyMMdd/yyyyMMddHHmmss
in Tokyo Standard Time. Generating this part worked fine and no problems arose. This is my code:
' Convert the time to Tokyo Standard Time
Dim japanTime = System.TimeZoneInfo.ConvertTime(Now, TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time"))
'Convert date to url that can be used in the Monitor url
Dim jTime_url As String = Convert.ToDateTime(japanTime.ToString()).ToString("yyyyMMdd/yyyyMMddHHmmss")
'BYTE ARRAY HOLDS THE DATA
Try
PictureBox1.Load("www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/" + jTime_url + ".jma_s.gif")
Console.WriteLine("http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/" + jTime_url + ".jma_s.gif")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
The problem I ran into here was that I may have requested the latest file too quickly, so the web server would always report "404 file not found"
.
Try 2: I tried delaying the file capture by 4 seconds. This worked, most of the time. The problems were that the file obtained from the web server only worked for most of the time due to that fact that once the "second" value reached 0
, the outputted string would be -4
instead of 56
.
The second problem was that the entire code wouldn't work sometimes randomly, just returning the error "404 file not found"
.
I tried outputting the Urls to the console and I viewed those online images in my web browser, and they worked just fine every time. The third problem was that I needed the "seconds" converted to be outputted as 00
, 01
, 02
etc.
Dim japanTime = System.TimeZoneInfo.ConvertTime(Now, TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time")) 'Convert user computer time to TokyoTime (japan)
'For debugging purposes only (not enabled)
' MessageBox.Show(japanTime.ToString())
'FORMAT OUTPUTTED: 1/19/2020 1:47:18 PM
'Needed format: yyyyMMdd/yyyyMMddHHmmss
Dim oldsecond As String = Convert.ToDateTime(japanTime.ToString()).ToString("ss") 'Create the old second to make a delay (source code not right)
' Dim oldminute As String = Convert.ToDateTime(japanTime.ToString()).ToString("mm")
'The -1 Explains the code delay
Dim newsecond As String = oldsecond - 5
Dim newnewsecond As String
If newsecond = -4 Then
newnewsecond = "56"
Else
If newsecond = -3 Then
newnewsecond = "57"
Else
If newsecond = -2 Then
newnewsecond = "58"
Else
If newsecond = -1 Then
newnewsecond = "59"
Else
If newsecond = 0 Then
newnewsecond = "00"
Else
newnewsecond = newsecond
End If
End If
End If
End If
End If
Label4.Text = newnewsecond
Dim jTime_url As String = Convert.ToDateTime(japanTime.ToString()).ToString("yyyyMMdd/yyyyMMddHHmm" & newnewsecond) 'Convert date to url that can be used in the Monitor url
Dim MyWebClient As New System.Net.WebClient
Try
PictureBox1.Load("www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/" + jTime_url + ".jma_s.gif")
Console.WriteLine("http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/" + jTime_url + ".jma_s.gif")
Catch ex as Exception
Console.Writeline(ex.message)
End Try
What I am trying to achieve here is that I want to be able to display this image source Url using the latest date information replacing yyyyMMdd/yyyyMMddHHmmss
and displaying it in a PictureBox.
Is there an error in my code, can I improvement it or do I have to re-write it in another way, shape, or form?
http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/yyyyMMdd/yyyyMMddHHmmss.jma_s.gif
Sample url image source you can look at:
http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/20200304/20200304081359.jma_s.gif
I changed the DateTime conversion method, using TimeZoneInfo.ConvertTimeBySystemTimeZoneId, passing my Local TimeZone.Id and "Tokyo Standard Time"
as parameters, to generate a DateTimeOffset that represents the current Tokyo DateTime.
Using a Timer, subtracting 4 seconds from the calculated DateTimeOffset (DateTimeOffset.AddSeconds(-4)
), the Images are loaded correctly.
► Note that the System clock must be synchronized with a NTP Server. 4
seconds is a relatively loose gap, but an unsynched clock will of course compromise the result anyway.
EDIT:
Changed System.Windows.Forms.Timer
to System.Timers.Timer
, since what takes more time here is the image download.
Using BeginInvoke()
to set the PictureBox.Image
, which takes almost nothing, prevents the UI from stuttering when the Form is moved around.
Private tokyoTimer As System.Timers.Timer = Nothing
Private tokyoClient As WebClient
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
tokyoTimer = New System.Timers.Timer() With {.Interval = 1000}
tokyoClient = New WebClient()
AddHandler tokyoTimer.Elapsed,
Sub()
Dim TokyoOffset = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(Date.Now, TimeZoneInfo.Local.Id, "Tokyo Standard Time")
Dim currentImage As String = TokyoOffset.AddSeconds(-4).ToString("yyyyMMdd/yyyyMMddHHmmss") & ".jma_s.gif"
Try
Dim data = tokyoClient.DownloadData(New Uri($"http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/{currentImage}"))
BeginInvoke(New MethodInvoker(
Sub()
PictureBox1.Image?.Dispose()
PictureBox1.Image = Image.FromStream(New MemoryStream(data))
End Sub))
Catch ex As Exception
' The exception hadling can be quite extensive here, since many factor can cause it:
' No server response, no Internet connection, internal server (500+) faults etc.
Console.WriteLine(ex.Message)
End Try
End Sub
tokyoTimer.Enabled = True
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
tokyoTimer.Enabled = False
tokyoTimer.Dispose()
tokyoClient?.Dispose()
End Sub
An asynchronous version of the same procedure, all contained in a class object.
The TokyoImagesDownloader
class exposes two public methods:
▶ StartDownload()
expects as arguments:
404 - Not found
because the requested image is not yet ready.A StopWatch
is used to synchronize the requested Interval between downloads, considering the time needed to download and show an image, so the clock shown in the Image itself should reflect the Interval requested.
▶ StopDownload()
can be called at any time to stop the download of the images.
With:
StartDownload(PictureBox1, 1, 8)
the class is instructed to show the images in PictureBox1
, download an image each second and delay the current Tokyo Time by 8 seconds.
Dim imageDonwloder As TokyoImagesDownloader = Nothing
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
imageDonwloder = New TokyoImagesDownloader()
imageDonwloder.StartDownload(PictureBox1, 1, 8)
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
imageDonwloder.StopDownload()
End Sub
Imports System.IO
Imports System.Net
Public Class TokyoImagesDownloader
Private tokyoClient As WebClient
Private cts As CancellationTokenSource = Nothing
Public Sub StartDownload(canvas As PictureBox, intervalSeconds As Integer, serverTimeDelaySeconds As Integer)
cts = New CancellationTokenSource()
tokyoClient = New WebClient()
Task.Run(Function() DownloadAsync(canvas, intervalSeconds, serverTimeDelaySeconds))
End Sub
Private Async Function DownloadAsync(canvas As PictureBox, intervalSeconds As Integer, serverTimeDelaySeconds As Integer) As Task
Dim downloadTimeWatch As Stopwatch = New Stopwatch()
downloadTimeWatch.Start()
Do
If cts.IsCancellationRequested Then Return
Dim TokyoOffset = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(Date.Now, TimeZoneInfo.Local.Id, "Tokyo Standard Time")
Dim currentImage As String = TokyoOffset.AddSeconds(-serverTimeDelaySeconds).ToString("yyyyMMdd/yyyyMMddHHmmss")
Dim url = New Uri($"http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/{currentImage}.jma_s.gif")
Try
Dim data = Await tokyoClient.DownloadDataTaskAsync(url)
canvas.BeginInvoke(New MethodInvoker(
Sub()
canvas.Image?.Dispose()
canvas.Image = Image.FromStream(New MemoryStream(data))
End Sub))
Await Task.Delay((intervalSeconds * 1000) - CInt(downloadTimeWatch.ElapsedMilliseconds))
downloadTimeWatch.Restart()
Catch wEx As WebException
Console.WriteLine(wEx.Message)
End Try
Loop
End Function
Public Sub StopDownload()
cts.Cancel()
tokyoClient?.CancelAsync()
tokyoClient?.Dispose()
cts?.Dispose()
End Sub
End Class