通常ですと、Azure上にWebサイトをホストする場合には、Visual Studio等で配置用のパッケージを作成し管理ポータルから配置するか、Visual Studioから配置する事になりますが、静的なサイトなどの場合には少しだけコンテンツを変更したいのに、このような煩雑な手順を踏む必要がありあまりお手軽とは言えないと思います。
また、現在githubで公開されている、「Windows Azure Accelerator for Web Roles」を利用(Windows Azure Accelerator for Web Rolesで簡単ホスティングを参照)しますと動的に構成を変えたりする事が比較的簡単にできるのですが、同様に少しだけコンテンツを変更したい程度の用途には大袈裟過ぎるきらいがあると感じます。
そこで、今回は少量のコンテンツ(静的なHTMLファイル等)をメモ帳等で外部で変更できるようにし、それをAzureのWebRole上に反映できる仕組みのサンプルを示します。
WebRoleとのデータ共有の為に、今回はWebDAVが利用可能な無料オンラインストレージの「クラウド型の無料オンラインストレージサービス ドライビー」を利用しました。(SkyDriveのWebDAVでの利用方法も紹介してきましたが、エクスプローラ上でhtmlファイルが作成できない等、色々と制約が多いので今回は利用していません。)
まずは、実行例です。
ローカルPCでWebDAVを利用してネットワークドライブとしてマウントした状態です。とりあえず、index.htmだけを配置してあります。
このhtmlファイルをメモ帳で開きました。
Azureに配置したWebRoleをブラウザで開きました。
メモ帳で少し追記して保存します。
少し待ってブラウザ画面を更新すると変更が反映されています。
いかがでしょうか、お手軽に更新出来る事がおわかり頂けたかと思います。
実際のサンプルコードは以下のようになっています。かなり手抜きなので、そのまま実運用するには足りない部分がかなりあると思いますので、その辺は適宜実装してください。
全体のプロジェクト構成です。今回はASP.NET Webロールをそのまま利用し、IISに対してServerManagerで仮想ディレクトリを追加しています。
下記は、/startup/startup.cmdです。Azureは既定ではWebDAVを利用する事が出来ませんので、StartupTaskでインストールを行います。
@echo off
if "%EMULATED%"=="true" goto :NEXT
ServerManagerCmd.exe -install Desktop-Experience -restart
:NEXT
exit /b 0
以下は、ServiceDefinition.csdefです。LocalStorage定義はIISの仮想ディレクトリとして利用しています。
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="NetworkDriveWebRole" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole" vmsize="ExtraSmall">
<Runtime executionContext="elevated"></Runtime>
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
</Endpoints>
<Imports>
<Import moduleName="RemoteAccess" />
<Import moduleName="RemoteForwarder" />
</Imports>
<LocalResources>
<LocalStorage name="WebRoot" cleanOnRoleRecycle="true" sizeInMB="1024" />
</LocalResources>
<Startup>
<Task executionContext="elevated" commandLine="startup\startup.cmd" taskType="foreground">
<Environment>
<Variable name="EMULATED">
<RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
</Variable>
</Environment>
</Task>
</Startup>
</WebRole>
</ServiceDefinition>
以下は、WebRole.csです。ネットワークドライブマウント、IIS構成、ネットワークドライブ上のファイル監視と仮想ディレクトリにマッピングしたLocalStorageへの反映等を行っています。
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Web.Administration;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace WebDavWebRole
{
public class WebRole : RoleEntryPoint
{
//ネットワークドライブレター
private static string _netMapPath = "O:";
private string _localVDPath;
private IList<FileInfo> _finfo;
public override bool OnStart()
{
try
{
//ServiceDefinition.csdefで設定した、LocalStorageの物理パスを取得
// <LocalStorage name="WebRoot" cleanOnRoleRecycle="true" sizeInMB="1024" />
var LocalResource = RoleEnvironment.GetLocalResource("WebRoot");
_localVDPath = LocalResource.RootPath;
//ネットワークドライブマウント
connectNetDrive();
_finfo = new List<FileInfo>();
//ネットワークドライブ内のファイルをローカルリソースに初期コピー
//サンプルなのでルートディレクトリ内のファイルのみを対象
var fs = Directory.EnumerateFiles(_netMapPath + @"\");
foreach (var f in fs)
{
var fname = Path.GetFileName(f);
var targetfname = _localVDPath + fname;
File.Copy(f, targetfname, true);
//元ファイル情報
var finfo = new FileInfo(f);
_finfo.Add(finfo);
}
//ローカルリソースの仮想パスをIISに設定
iisConfigure();
}
catch
{
}
return base.OnStart();
}
public override void Run()
{
try
{
while (true)
{
//ネットワークドライブ変更監視
fileSystemWatch();
//監視間隔は適時変更
Thread.Sleep(5 * 1000);
}
}
catch
{
}
}
/// <summary>
/// IIS構成
/// 仮想ディレクトリに対しLocalStorageの物理パスを設定
/// </summary>
private void iisConfigure()
{
try
{
using (ServerManager serverManager = new ServerManager())
{
var sitename = RoleEnvironment.CurrentRoleInstance.Id + "_Web";
var vdPath = "/vd";
//IISconfigureの構成を完了待合せ
var isLoop = true;
while (isLoop)
{
try
{
foreach (var target_site in serverManager.Sites)
{
if (target_site.Name.StartsWith(sitename))
{
isLoop = false;
break;
}
}
}
catch (Exception)
{
isLoop = true;
}
if (isLoop)
Thread.Sleep(1000);
}
var site = serverManager.Sites[sitename];
foreach (var app in site.Applications)
{
app.VirtualDirectories.Add(vdPath, _localVDPath);
}
serverManager.CommitChanges();
}
}
catch
{
}
}
/// <summary>
/// ファイルシステムの監視
/// サンプルなのでリモートファイルの
/// 存在チェックと更新だけをチェック
/// </summary>
void fileSystemWatch()
{
foreach (var f in _finfo)
{
var f_LastWriteTime = f.LastWriteTime;
var f_Length = f.Length;
f.Refresh();
if (f.Exists)
{
//最終更新日時が変更されていればコピー
if (f_LastWriteTime != f.LastWriteTime)
f.CopyTo(_localVDPath + f.Name, true);
}
else
{
File.Delete(_localVDPath + f.Name);
_finfo.Remove(f);
}
}
}
/// <summary>
/// WIN32API(WNetAddConnection2)でネットワークドライブマウント
/// </summary>
private void connectNetDrive()
{
var netRes = new NETRESOURCE();
netRes.dwScope = 0;
netRes.dwType = 1;
netRes.dwDisplayType = 0;
netRes.dwUsage = 0;
netRes.lpLocalName = _netMapPath;
netRes.lpRemoteName = "https://youraccount.drivee.jp";
netRes.lpProvider = "";
var userId = "youraccount";
var password = "yourpassword";
var ret = WNetAddConnection2(ref netRes, password, userId, 0);
}
[DllImport("mpr.dll", EntryPoint = "WNetAddConnection2",
CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
private static extern int WNetAddConnection2(
ref NETRESOURCE lpNetResource, string lpPassword, string lpUsername, Int32 dwFlags);
[StructLayout(LayoutKind.Sequential)]
internal struct NETRESOURCE
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpLocalName;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpRemoteName;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpComment;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpProvider;
}
}
}
SkyDriveSimpleViewer.exeを実行させるとエラーになります。どうすればいいでしょうか?
OSはXPです。