Wenn die Cam (hier: IN-5907 HD) zahlreiche Alarmbilder mit FTP auf einen Webserver hochlädt, ist sehr schnell ein Recherche-Tool erforderlich, mit dem man im Webbrowser aus einer Vorschau der aufgenommenen Bilder zur Großansicht wechseln und nach Datum filtern kann. Die nachfolgende Lösung wurde auf Basis C#/ASP.NET 4.x entwickelt und läuft auf einem Windows (2012) Server. Das besondere an dieser Lösung ist, dass sie die hochauflösenden Bilder bereits serverseitig verkleinert und nur die verkleinerten Vorschaubilder an den Browser liefert. Das reduziert bei tausenden von Alarmbildern den Traffic enorm. Durch Mausklick auf die Vorschau erhält man das originale, hochauflösende Bild.
So sieht das dann aus:
Hier der Sourcecode.
default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DirectoryListing.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Directory Listing</title>
<link href="./style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="LabelDropDownList" runat="server" Text="Datumsfilter: "></asp:Label>
<asp:DropDownList ID="DropDownListDate" onselectedindexchanged="DropDownListDate_SelectedIndexChanged" runat="server"></asp:DropDownList>
<asp:GridView ID="GridViewPreview" onpageindexchanging="GridViewPreview_PageIndexChanging" runat="server"></asp:GridView>
</div>
</form>
</body>
</html>
default.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Collections;
using System.Data;
namespace DirectoryListing
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DataTable dt = GetFilesAndFolders();
Session["DATATABLE"] = dt;
Session["DATAVIEW"] = GetDataView(dt, new DateTime(1, 1, 1), 0);
}
}
public DataTable GetFilesAndFolders()
{
DirectoryInfo dirInfo = new DirectoryInfo(DirectoryListingLib.getImagePath());
FileInfo[] fileInfo = dirInfo.GetFiles("*.jpg", SearchOption.TopDirectoryOnly);
DataTable dt = new DataTable();
DataRow dr;
DataColumn dc = new DataColumn("ID", typeof(System.Int64));
dc.AutoIncrement = true;
dt.Columns.Add(dc);
dt.Columns.Add("Create_Date", typeof(DateTime));
dt.Columns.Add("Create_DateTime", typeof(DateTime));
for (int i = 0; i < DirectoryListingLib.NUM_COLUMNS; i++)
{
dt.Columns.Add("Preview_Pic" + i.ToString(), typeof(String));
}
dt.DefaultView.Sort = "Create_DateTime DESC";
int colCounter = -1;
dr = dt.NewRow();
foreach (FileInfo file in fileInfo)
{
if (colCounter++ >= DirectoryListingLib.NUM_COLUMNS - 1)
{
dt.Rows.Add(dr);
colCounter = 0;
dr = dt.NewRow();
}
dr["Create_Date"] = file.CreationTime.ToString("dd.MM.yyyy"); // Bind to DropDownBox
dr["Create_DateTime"] = file.CreationTime.ToString("dd.MM.yyyy HH:mm:ss"); // For sorting only
dr["Preview_Pic" + colCounter.ToString()] = "<a href=\"" + DirectoryListingLib.getImageVPath() + "/" + file.Name +
"\"><img src=\"./getPreviewImage.aspx?img=" + Server.UrlEncode(file.Name) + "\"></a><br>" + file.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
}
dt.Rows.Add(dr);
return dt;
}
public DataView GetDataView(DataTable dt, DateTime selectedDateTime, int selectedIndexDropDownListDate)
{
EnumerableRowCollection<DataRow> query = from rows in dt.AsEnumerable()
where rows.Field<DateTime>("Create_DateTime").Date == selectedDateTime.Date || selectedDateTime.Date.Year < 1970
orderby rows.Field<DateTime>("Create_DateTime") descending
select rows;
DataView dv = query.AsDataView();
if (dv.Count > 0)
{
this.GridViewPreview.Columns.Clear();
foreach (DataColumn col in dt.Columns)
{
//Declare the bound field and allocate memory for the bound field.
BoundField bfield = new BoundField();
bfield.DataField = col.ColumnName;
bfield.HeaderText = col.ColumnName;
bfield.HtmlEncode = false;
this.GridViewPreview.Columns.Add(bfield);
}
this.GridViewPreview.BackColor = System.Drawing.Color.LightGray;
this.GridViewPreview.AutoGenerateColumns = false; // Avoid additional columns
this.GridViewPreview.AllowPaging = true;
this.GridViewPreview.AllowSorting = true;
this.GridViewPreview.PageSize = 20;
this.GridViewPreview.PageIndex = 0;
this.GridViewPreview.PagerSettings.Mode = PagerButtons.NumericFirstLast;
this.GridViewPreview.PagerSettings.Position = PagerPosition.TopAndBottom;
this.GridViewPreview.Columns[0].Visible = false; // Hide ID
this.GridViewPreview.Columns[1].Visible = false; // Hide Date
this.GridViewPreview.Columns[2].Visible = false; // Hide DateTime
this.GridViewPreview.ShowHeader = false;
this.GridViewPreview.CssClass = "DataTable";
this.GridViewPreview.DataSource = dv;
this.GridViewPreview.DataBind();
this.DropDownListDate.DataSource = dt.DefaultView.ToTable(true, "Create_Date");
this.DropDownListDate.DataTextField = "Create_Date";
this.DropDownListDate.DataTextFormatString = "{0:dd.MM.yyyy}";
this.DropDownListDate.DataValueField = "Create_Date";
this.DropDownListDate.AutoPostBack = true;
this.DropDownListDate.DataBind();
this.DropDownListDate.Items.Insert(0, new ListItem("Alle", "01.01.0001"));
this.DropDownListDate.SelectedIndex = selectedIndexDropDownListDate;
}
return dv;
}
public void GridViewPreview_PageIndexChanging(Object sender, GridViewPageEventArgs e)
{
this.GridViewPreview.DataSource = (DataView)Session["DATAVIEW"];
this.GridViewPreview.PageIndex = e.NewPageIndex;
this.GridViewPreview.DataBind();
}
public void DropDownListDate_SelectedIndexChanged(Object sender, EventArgs e)
{
string stringDateTime = this.DropDownListDate.SelectedItem.ToString();
DateTime tempDateTime;
DateTime.TryParse(stringDateTime, out tempDateTime);
GetDataView((DataTable)Session["DATATABLE"], tempDateTime, this.DropDownListDate.SelectedIndex);
}
}
public static class DirectoryListingLib
{
public static int NUM_COLUMNS = 4; // Number of preview columns
public static bool IsProductiveEnvironment() // Check environment (test or productive)
{
string hostName = HttpContext.Current.Request.ServerVariables["SERVER_NAME"].ToLower();
return !hostName.StartsWith("localhost");
}
public static string getImagePath() // Absolute path of pictures
{
return HttpContext.Current.Server.MapPath(DirectoryListingLib.getImageVPath());
}
public static string getImageVPath() // Virtual path of pictures
{
return (DirectoryListingLib.IsProductiveEnvironment() ? @"/webcam/img" : @"/images");
}
}
}
getPreviewImage.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="getPreviewImage.aspx.cs" Inherits="DirectoryListing.getPreviewImage" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
getPreviewImage.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
// using System.Web.UI.WebControls;
using System.Security;
using System.Net;
using System.Text;
using System.IO;
using System.Drawing;
namespace DirectoryListing
{
public partial class getPreviewImage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
String imgName = Request.QueryString["img"].Trim();
Response.Clear();
if (imgName.Length > 0)
{
String fileFullPath = DirectoryListingLib.getImagePath() + "/" + imgName;
if (File.Exists(fileFullPath))
{
Image imgObj = Bitmap.FromFile(fileFullPath);
imgObj = (Image)(new Bitmap(imgObj, new Size(128, 72)));
byte[] byteArray = imageToByteArray(imgObj);
// Image out to http....
Response.AddHeader("Content-Disposition", "attachment; filename=camimage.jpg");
Response.AddHeader("Content-Length", byteArray.Length.ToString());
Response.ContentType = "image/jpeg";
Response.BinaryWrite(byteArray);
}
}
Response.End();
}
// Convert image to byte array
private byte[] imageToByteArray(System.Drawing.Image image)
{
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
}
}
}
style.css
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
font-weight: normal;
}
.TableScroll
{
max-height: 275px;
overflow: auto;
border:1px solid #ccc;
}
.DataTable
{
border-collapse: collapse;
}
.DataTable tr th
{
background-color: #3c454f;
color: #ffffff;
padding: 5px 5px 5px 5px;
border: 1px solid #cccccc;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
font-weight: normal;
text-transform:capitalize;
}
.DataTable tr:nth-child(2n+2)
{
background-color: #f3f4f5;
}
.DataTable tr:nth-child(2n+1) td
{
background-color: #d6dadf;
color: #454545;
}
.DataTable tr td
{
padding: 5px 10px 5px 10px;
color: #454545;
font-family: Arial, Helvetica, sans-serif;
font-size: 11px;
border: 1px solid #cccccc;
vertical-align: middle;
text-align:center;
}
.DataTable tr td:first-child
{
text-align: center;
}
Als Erweiterung wäre z.B. eine AJAX-Funktion denkbar, die die großen Bilder als modales Fenster hochpoppen lässt.