◆ PowerShell を使う

HTML / JavaScript でなにか作ってるとき 基本的には直接 HTML ファイルを開いた file スキームでも見れるのですが一部機能は HTTP/HTTPS じゃないと使えません
HTTP でアクセスできるように静的ファイルを返すだけのローカルサーバをたてようにも準備ができていない環境もあります

スクリプト言語のランタイムがあるなら

Python や PHP が入ってるなら

py -m http.server 3000

python3 -m http.server 3000

php -S localhost:3000

のようなコマンドでお手軽に使えます

Node.js の場合は npm パッケージが必要で標準機能だとできません
プログラムで Web サーバを簡単に作れるので 固定の 1 ファイルだけならコードを書くのもありです
しかし apache 風にフォルダ指定でパスに応じたファイルとなると複雑なので serve とかをインストールするのがいいです

なにもないとき

こういうツールが何もなくて Python などのインストールはしたくない時があります

標準で動くものということでサーバを起動したいとなると出てくるのは .NET です
PowerShell を使って Web サーバを作ってみることにしました

中身は以前に C# で作った簡易 Web サーバをほぼそのまま移植しただけのものです

[serve.ps1]
try {
$listener = new-object Net.HttpListener
$listener.Prefixes.Add("http://+:8888/")
$listener.Start()
echo "Server is running at port 8888"

function ContentType ($ext)
{
switch ($ext)
{
".html" { "text/html" }
".js" { "text/javascript" }
".css" { "text/css" }
".json" { "application/json" }
".xml" { "text/xml" }
".gif" { "image/gif" }
".ico" { "image/x-icon" }
".jpg" { "image/jpeg" }
".png" { "image/png" }
".svg" { "image/svg+xml" }
".webp" { "image/webp" }
".zip" { "application/zip" }
".webp" { "image/webp" }
Default { "text/plain" }
}
}

while ($true)
{
$context = $listener.GetContext()
$path = $context.Request.Url.AbsolutePath
if ($path.EndsWith("/")) {
$path += "index.html"
}
$filepath = join-path (get-location) $path
$exists = [IO.File]::Exists($filepath)
echo "$($path) --> $($filepath) [$($exists)]"
if ($exists) {
$extension = [IO.Path]::GetExtension($filepath)
$context.Response.ContentType = ContentType($extension)
$rstream = [IO.File]::OpenRead($filepath)
$stream = $context.Response.OutputStream
$rstream.CopyTo($stream)
$stream.Close()
$rstream.Dispose()
} else {
$context.Response.ContentType = "text/html"
$context.Response.StatusCode = 404
$content = [Text.Encoding]::UTF8.GetBytes("File Not Found")
$stream = $context.Response.OutputStream
$stream.Write($content, 0, $content.Length)
$stream.Close()
}
}
} catch {
Write-Error $_.Exception
}

実行すると カレントディレクトリとドキュメントルートにして ポート 8888 で起動します

作った後に気づいたのですが PowerShell ってスクリプトにすると簡単に実行できないんですよね
bat のように簡単に使えればいいのですが ポリシー指定とか必要です

それも自動でするために bat ファイルを用意して bat ファイルを実行したら PowerShell が実行されるようにします

[serve.bat]
powershell -NoProfile -ExecutionPolicy Unrestricted %~dp0\serve.ps1
pause

注意点は Web サーバを起動するためには管理権限が必要です
bat ファイルを右クリックで「管理者として実行」を使って実行します

あともうひとつ注意点で Windows 7 の標準のバージョンだと動きません

PS C:\> [Environment]::Version

Major Minor Build Revision
----- ----- ----- --------
2 0 50727 8806

PS C:\> $PSVersionTable

Name Value
---- -----
CLRVersion 2.0.50727.8806
BuildVersion 6.1.7601.17514
PSVersion 2.0
WSManStackVersion 2.0
PSCompatibleVersions {1.0, 2.0}
SerializationVersion 1.1.0.1
PSRemotingProtocolVersion 2.1

PowerShell 2.0 なので .NET バージョンも古いのですよね
FileStream の CopyTo メソッドが存在しなかったです

csc と jsc でも

今回は PowerShell にしましたが ちょっと古いバージョンになるものの Windows 標準で csc や jsc が入ってるので C# や JScript.NET で書くこともできます
ただコンパイル必要なので ちょっと変更したいとかに対応しやすいのはスクリプト言語の方かなと思います

exe 公開でよかったかも?

作った環境で使う分にはこれでよかったのですが 他の環境で使ったりや記事を見た人が使うと考えると PowerShell はどうなんでしょう

bat の方ならともかく ps1 の方は見ながら自分で書くような長さじゃないですし ローカルエディタにコピペ必須です
直接 PowerShell に貼り付けてもいいですが bat ファイルも用意すると 2 ファイル必要です

結局 準備の面倒くささがあるのですよね
Python や PHP を入れたくないというのも理由の一つは準備の面倒くささですし
それなら C# で作ってビルドした exe を 1 ファイル公開してダウンロードできるようにすればその方が使いやすい気もしました
用途的にフォルダの指定機能とポート指定機能だけつければコードを変更する必要もとくにないでしょうし