正在 URL 中使用特定日期时间从 Internet 下载图像

Downloading an image from the Internet with specific DateTime in the URL

我正在尝试开发一个应用程序,它将使用生成的 Url 路径从 Web 服务器获取文件。
Web 服务器上每秒都会创建一个新文件,我正在尝试访问该文件并将其显示在 PictureBox 中(旧文件不会被删除)。
我 运行 遇到了一些问题,服务器返回 404 错误,但我不知道为什么。
该应用程序似乎无法使用生成的图像源 Url 下载文件,但是当我在网络浏览器(例如 Chrome、Internet Explorer)中访问生成的 link 时,它可以正常工作就好了。
在 URL 格式不正确的过程中,我也 运行 遇到了一些问题。

尝试 1: 我尝试使用 URl 生成器输出的字符串下载文件。 URl 必须采用日期格式:yyyyMMdd/yyyyMMddHHmmss(东京标准时间)。生成这部分工作正常,没有出现任何问题。这是我的代码:

' 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

我 运行 进入这里的问题是我可能请求最新文件的速度太快,所以网络服务器总是报告 "404 file not found"

尝试 2: 我尝试将文件捕获延迟 4 秒。这在大多数情况下都有效。问题是从网络服务器获得的文件只在大部分时间有效,因为一旦 "second" 值达到 0,输出的字符串将是 -4 而不是56.
第二个问题是整个代码有时 运行domly 无法正常工作,只是返回错误 "404 file not found".
我尝试将 Url 输出到控制台,然后在我的网络浏览器中查看这些在线图像,它们每次都运行良好。第三个问题是我需要将 "seconds" 转换为输出为 000102

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

我在这里想要实现的是,我希望能够使用最新的日期信息替换 yyyyMMdd/yyyyMMddHHmmss 并在 PictureBox 中显示它来显示此图像源 Url。

我的代码是否有错误,我可以改进它还是必须以其他方式、形状或形式重写它?

http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/yyyyMMdd/yyyyMMddHHmmss.jma_s.gif

示例url 图片来源你可以看看:
http://www.kmoni.bosai.go.jp/data/map_img/RealTimeImg/jma_s/20200304/20200304081359.jma_s.gif

我更改了日期时间转换方法,使用代表当前东京日期时间的 TimeZoneInfo.ConvertTimeBySystemTimeZoneId, passing my Local TimeZone.Id and "Tokyo Standard Time" as parameters, to generate a DateTimeOffset

使用计时器,从计算的 DateTimeOffset 中减去 4 秒 (DateTimeOffset.AddSeconds(-4)),图像已正确加载。

► 请注意,系统时钟必须与 NTP Server 同步。 4秒是一个相对宽松的差距,但不同步的时钟当然会影响结果。

编辑:
System.Windows.Forms.Timer 更改为 System.Timers.Timer,因为这里需要更多时间的是图片下载。
使用 BeginInvoke() 设置 PictureBox.Image,这几乎不需要任何操作,可以防止 UI 在表单移动时 卡顿

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



同一过程的异步版本,全部包含在一个 class 对象中。
TokyoImagesDownloader class 公开了两个 public 方法:

StartDownload() 期望参数:

  1. 用于显示下载图片的PicureBox控件
  2. 下载之间的间隔,以秒为单位。
  3. 延迟值(以秒为单位)减去当前东京当地时间,以防止服务器返回 404 - Not found 因为请求的图像尚未准备好。

一个StopWatch用于同步下载之间请求的Interval,考虑到下载和显示图像所需的时间,所以Image本身显示的时钟应该反映请求的Interval。

StopDownload() 可随时调用以停止下载图片。

有:

StartDownload(PictureBox1, 1, 8)

class 被指示显示 PictureBox1 中的图像,每秒下载一个图像并将当前东京时间延迟 8 秒。

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