从设备自动获取图片并上传到Web服务器





5.00/5 (2投票s)
如何创建 Windows Phone 应用,从相机定时拍照,并将它们上传到 Apache Web 服务器以供远程查看
范围
在本文中,我们将学习如何开发一个 Windows Phone 应用,该应用能够通过设备的相机自动拍照,并通过 Web 服务器将它们上传,以便可以通过浏览器查看。换句话说,我们将开发一种网络摄像头/监控摄像头,可以从智能手机或平板电脑运行,以便从其他地方监控特定位置。我们将使用 Visual Basic .NET 作为应用部分,而服务器端编码将使用 PHP 完成,正如我们在之前关于地理定位的文章中所做的那样(有关更多信息,请参阅 将设备地理定位并将其坐标存储在 Web 服务器上)。
必备组件
要使用文章中提供的代码,您需要
- Windows 8.1
- 安装了 Windows Phone 8.1 SDK 的 Visual Studio
- 本地或托管的 Web 服务器,带有启用了 PHP 支持的 Apache 。或者,Web 服务器部分可以使用 ASP/ASP.NET 编写。
配置上述每个先决条件超出了本文的范围,因此我将不讨论这一点,假设所有内容都已成功配置并运行。如果您需要有关如何安装 Apache 加 PHP/MySQL 服务器的简要参考,您可以参考我的文章 在 Debian 或 Ubuntu Linux 上快速安装 LAMP 服务器。
引言
在本文中,我将介绍一种实现智能手机/平板电脑基础网络摄像头的简单方法,该方法可用于设置可以演变成监控应用程序等的场景。对物联网应用领域的日益关注使我们越来越关注和认识到我们日常使用的设备的可互连性,我认为目前这是一个朝着这个方向迈出的(尽管非常基础)的良好练习。
我们在这里要做的是使用 Windows 设备作为自动相机,每隔预定秒数拍摄照片作为数据流,将其远程发送到 PHP 脚本,该脚本将其作为文件巩固在远程 Web 服务器上,使其可以在任何允许 Internet 连接的地方查看。
让我们从服务器部分开始,以便做一些初步假设,这将使应用开发更清晰。
服务器端脚本
如前所述,我们将使用 PHP(和 Apache Web 服务器)来设置一个简单的脚本,该脚本能够接收通过 POST
方法发送的文件,然后存储它,从而使其后续可供进一步的 HTTP 请求访问,例如浏览器导航。该脚本尽可能简单:它包含接收 POST
内容(即“file
”参数),然后打开一个任意文件(我将其命名为“test.jpg”),以二进制模式写入其中。我们来看看它
<?php
$content = $_POST['file'];
$sfile = fopen("test.jpg", 'wb'); // 'wb' parameter means "write binary"
fwrite($sfile, $content);
fclose($sfile);
?>
只需最少的 PHP 知识即可轻松理解。
接下来,我们将准备另一个页面,我将其命名为“index.html”:一个纯 HTML 文件,其中将显示我们的“test.jpg”。
<title>Network Camera</title>
<!-- Omissis: styles will be present in the full code -->
<h1>Network Camera</h1>
<div id="imgwrapper">
<img src="test.jpg">
</div>
总结我们项目这部分,我们讨论的是拥有一个配备两个简单脚本的 Web 服务器。第一个脚本将由我们的 Windows Phone 应用利用,它将接收并远程存储应用程序本身发送的数据流,而第二个脚本只是这些数据的查看器,通过 IMG
标签封装发送的数据(即照片),以允许用户查看它。现在我们可以实现我们的应用程序来完成工作。
Windows Phone 自动相机应用
在本节中,我们将开发我们的 Windows Phone 应用,它将包含以下内容
- 自动相机拍摄
- 自动照片上传(即,将照片提交到我们的 upload.php 页面)
我们将使用 Windows.Media.MediaProperties 命名空间
来访问我们设备的相机功能。
首先,打开 Visual Studio,然后从Visual Basic 模板菜单中选择Store Apps / Blank App (Windows Phone) 模板。它将为我们创建一个基于单个页面(即 MainPage.xaml)的简单布局,我们将在其中应用所需的控件和业务逻辑。
让我们看看我们将在 XAML 中包含哪些元素
首先,我们有一个 CaptureElement
,即一个我们将在此控件中渲染相机预览的控件,以便观察将作为图像捕获的内容。一个名为 txtRemUP
的 TextBox
将包含我们 upload.php 的远程地址(如果我们的应用必须与不同主机通信,而不是硬编码 URI,则很有用)。一个 ToggleButton
将允许我们启动和停止图像采集,最后我们有两个 TextBlock
用于日志目的(最后一次拍摄和上传的日期和时间),以及一个 Canvas
来显示最后一张拍摄的照片。
应用先决条件
由于我们的应用程序必须要求使用硬件设备和网络通信,因此我们必须相应地设置应用程序的功能和要求。
因此,我们必须双击 Package.appxmanifest 文件(由 Visual Studio 创建),访问“功能”和“要求”选项卡,并将相机和网络标志设置为如下
相机初始化
在代码方面,第一件事是在应用程序启动时初始化我们的相机,相应地设置 CaptureElement
源属性。请注意,几乎所有将要呈现的事件都将是异步的。让我们从 OnNavigatedTo
事件开始,该事件定义了某个 Page
将被显示的时刻。
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
cam = New Windows.Media.Capture.MediaCapture
Await cam.InitializeAsync()
mCanvas.Source = cam
Await cam.StartPreviewAsync()
End Sub
我已经将一个 MediaCapture
变量 cam
声明为对整个作用域可见。在这里,我们将对其使用 InitializeAsync
方法来初始化我们的相机,将 MediaCapture
变量绑定为我们 CaptureElement
控件的源。进一步调用 StartPreviewAsync
方法将在控件上以图形方式渲染相机采集。
在我们的 ToggleButton
的 Toggled
事件上,我们将根据 ToggleButton
的状态来控制 Timer
的启动或处置。
If toggler.IsOn Then
t = New System.Threading.Timer(AddressOf Shoot, Nothing, TimeSpan.Zero, New TimeSpan(0, 0, 10))
t.Change(30000, Threading.Timeout.Infinite)
Else
If Not (t Is Nothing) Then t.Dispose()
End If
如果 ToggleButton
处于活动状态,我们将创建一个 Timer
的新实例,将其间隔设置为 30 秒。每个间隔都会调用子例程 Shoot
。在该子例程中,我们将控制照片的采集以及其上传。
照片采集
Shoot
子例程如下
Public Async Sub Shoot(sender As Object)
Dim imgFormat As ImageEncodingProperties = ImageEncodingProperties.CreateJpeg()
stream = New Streams.InMemoryRandomAccessStream
Await cam.CapturePhotoToStreamAsync(imgFormat, stream)
Await stream.FlushAsync
stream.Seek(0)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync_
(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf SetCanvas)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync_
(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf MarshalToggler)
End Sub
在此,我们定义了一个 RandomAccessStream
,它将通过调用 CapturePhotoToStreamAsync
方法(来自 MediaCapture
变量)来初始化。简单来说,调用该方法将访问相机进行拍摄,并将图像复制到流中。在我们的例程结束时,您可以看到两个 Dispatcher
调用到两个子例程,后者只是检查 ToggleButton
的状态。然而,第一个调用 SetCanvas
例程,它将被用于将图像保存到我们的 Canvas
,但更重要的是,通过 POST
请求到我们的 upload.php URI 将其上传到我们的 Web 服务器。
Private Async Sub SetCanvas()
tmpLab.Text = DateTime.Now.ToString
Dim b As New BitmapImage()
Await b.SetSourceAsync(stream)
pCanvas.Source = b
Dim io As Stream = stream.AsStreamForRead
Dim buf(io.Length) As Byte
io.Position = 0
Await io.ReadAsync(buf, 0, io.Length)
Dim httpClient As New HttpClient
Dim form = New MultipartFormDataContent
form.Add(New ByteArrayContent(buf, 0, buf.Length), "file")
Dim response = Await httpClient.PostAsync(txtRemUP.Text, form)
response.EnsureSuccessStatusCode()
httpClient.Dispose()
Dim sd As String = response.Content.ReadAsStringAsync().Result
lastSent.Text = Date.Now.ToString
End Sub
除了用于日志记录的 TextBlock
s 之外,此例程还将执行以下操作
- 创建
BitmapImage
对象:通过SetSourceAsync
方法,使用我们的RandomAccessStream
创建一个新的Bitmap
,并将其用作我们Canvas
的Source
,以显示最后一张拍摄的照片。 - 创建简单的
Stream
以访问我们原始RandomAccessStream
(我们的图像)中的Byte
数组。这将是正确上传我们数据所必需的。 - 创建
MultipartFormDataContent
(即一个“模拟”的 HTTP 表单,其中包含 Web 等待的变量,我们在创建 upload.php 页面时将其命名为“file”)。 - 物理上传我们的图像。
完整的 MainPage
后置代码如下
Imports System.Net.Http
Imports Windows.Storage
Imports Windows.Media.MediaProperties
Public NotInheritable Class MainPage
Inherits Page
Dim t As System.Threading.Timer
Dim cam As Windows.Media.Capture.MediaCapture
Dim stream As Streams.InMemoryRandomAccessStream
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
cam = New Windows.Media.Capture.MediaCapture
Await cam.InitializeAsync()
mCanvas.Source = cam
Await cam.StartPreviewAsync()
End Sub
Private Async Sub SetCanvas()
tmpLab.Text = DateTime.Now.ToString
Dim b As New BitmapImage()
Await b.SetSourceAsync(stream)
pCanvas.Source = b
Dim io As Stream = stream.AsStreamForRead
Dim buf(io.Length) As Byte
io.Position = 0
Await io.ReadAsync(buf, 0, io.Length)
Dim httpClient As New HttpClient
Dim form = New MultipartFormDataContent
form.Add(New ByteArrayContent(buf, 0, buf.Length), "file")
Dim response = Await httpClient.PostAsync(txtRemUP.Text, form)
response.EnsureSuccessStatusCode()
httpClient.Dispose()
Dim sd As String = response.Content.ReadAsStringAsync().Result
lastSent.Text = Date.Now.ToString
End Sub
Private Sub MarshalToggler()
If toggler.IsOn Then
t = New System.Threading.Timer(AddressOf Shoot, Nothing, TimeSpan.Zero, _
New TimeSpan(0, 0, 10))
t.Change(30000, Threading.Timeout.Infinite)
Else
If Not (t Is Nothing) Then t.Dispose()
End If
End Sub
Public Async Sub Shoot(sender As Object)
Dim imgFormat As ImageEncodingProperties = ImageEncodingProperties.CreateJpeg()
stream = New Streams.InMemoryRandomAccessStream
Await cam.CapturePhotoToStreamAsync(imgFormat, stream)
Await stream.FlushAsync
stream.Seek(0)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync_
(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf SetCanvas)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync_
(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf MarshalToggler)
End Sub
Private Sub toggler_Toggled(sender As Object, e As RoutedEventArgs)
MarshalToggler()
End Sub
End Class
让我们看一个项目执行的示例。
整体测试
源代码
本文使用的完整源代码可在此处下载: https://code.msdn.microsoft.com/Get-Automatic-Pictures-3b3875dd。