1699721826
2023-11-10 19:45:00
編集者は、静的サイトの出力を定期的に CMS 12 メディア システムに入れる必要があります。 これが私が検討したアイデアの 1 つです。
出力は ZIP ファイルとして配信されたため、その 1 つのファイルをアップロードするだけで済む予定でした。
アーカイブ内の構造はこんな感じでした。
-
コンテンツ
- 資産
-
ライブラリ
- player-0.0.11.min.js
- site-e34Ade.min.css
- インデックス.html
そのファイルがメディア システム フォルダーにアップロードされると、エディターは新しいページ タイプでそのファイルを参照します。
まず、ZIP ファイル用に別のメディア データ タイプを追加しました。
[ContentType(GUID = "3ca219e6-10ed-4014-88d1-a884f009a571")]
[MediaDescriptor(ExtensionString = "zip")]
public class ZipFile : MediaData
次に、HostPageType と名付けた新しいページ タイプです。
[AllowedTypes(new[] { typeof(ZipFile) })]
public virtual ContentReference ZipFileReference { get; set; }
残りのパスを「無視」し、後続のパスのバリエーションをすべて取得するための部分ルーターは、HostPageController の Index() メソッドによって処理されます。
public class HostPagePartialRouter : IPartialRouter<HostPageType, HostPageType>
{
public object RoutePartial(HostPageType content, UrlResolverContext segmentContext)
{
segmentContext.RemainingSegments = ReadOnlyMemory<char>.Empty;
return content;
}
public PartialRouteData GetPartialVirtualPath(HostPageType content, UrlGeneratorContext urlGeneratorContext)
{
return null;
}
}
その後、コントローラーがほとんどの作業を行います。 パス値、マッチングを処理し、ZIP ファイル内からコンテンツをストリーミングします。
書いた上で 私自身の静的サイトジェネレーター、MIME タイプの検出などのことは、私が以前から行っていたことです。
using System;
using System.IO;
using System.IO.Compression;
using EPiServer;
using EPiServer.Core;
using EPiServer.Web.Mvc;
using EPiServer.Web.Routing;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Net.Http.Headers;
..
[HttpGet]
public ActionResult Index(HostPageType currentPage)
{
if(ContentReference.IsNullOrEmpty(currentPage.ZipFileReference))
{
return this.Content("ZIP-file is missing.", "text/plain");
}
var url = this.urlResolver.GetUrl(currentPage.ContentLink);
var pageUrl = url.Contains("://", StringComparison.Ordinal) ? new Uri(url).AbsolutePath : url;
var currentUrl = this.Request.Path.Value ?? string.Empty;
if(currentUrl.Equals(pageUrl.TrimEnd("https://krompaco.nu/"), StringComparison.CurrentCultureIgnoreCase))
{
return this.RedirectPermanent(pageUrl);
}
var remainingPath = currentUrl.Replace(pageUrl, string.Empty, StringComparison.CurrentCultureIgnoreCase);
var zipFileContent = this.contentLoader.Get<Shared.Media.ZipFile>(currentPage.ZipFileReference);
var blob = zipFileContent.BinaryData;
using var stream = blob.OpenRead();
using var archive = new ZipArchive(stream, ZipArchiveMode.Read);
foreach (var entry in archive.Entries)
{
var nameWithOutContentPrefix = entry.FullName.StartsWith("content/", StringComparison.OrdinalIgnoreCase)
? entry.FullName.Substring(8)
: entry.FullName;
if(((string.IsNullOrEmpty(remainingPath)
&& nameWithOutContentPrefix.Equals("index.html", StringComparison.Ordinal)))
|| (nameWithOutContentPrefix.EndsWith(".html", StringComparison.OrdinalIgnoreCase)
&& nameWithOutContentPrefix.Equals(remainingPath, StringComparison.CurrentCulture)))
{
using var sr = new StreamReader(entry.Open());
var html = sr.ReadToEnd();
html = html
.Replace(
"""<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>""",
string.Empty,
StringComparison.CurrentCultureIgnoreCase);
return this.Content(html, "text/html");
}
if (nameWithOutContentPrefix.Equals(remainingPath, StringComparison.CurrentCulture))
{
var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.TryGetContentType(nameWithOutContentPrefix, out var contentType);
using var entryStream = entry.Open();
var ms = new MemoryStream();
entryStream.CopyTo(ms);
ms.Position = 0;
this.Response.Headers[HeaderNames.CacheControl] = "public, max-age=3600";
return new FileStreamResult(ms, contentType ?? "text/plain");
}
}
return this.Content(currentPage.Name, "text/plain");
}
これで、ZIP のコンテンツ フォルダーのindex.html がホスト ページの HTML 応答として出力され、静的サイト内のすべてのパスが相対パスであるため、すべてのリソースが読み込まれます。
今のところ、これは単なる概念実証です。 これをさらに進めるには、おそらく、ZIP ファイルのハンドルが多すぎることから、何らかのキャッシュやその他の種類の保護を追加することを意味します。
HtmlAgilityPack のようなものを追加して、HTML をより適切な方法で処理することもできます。
#MediaData #ZIP #ファイル内から静的サイト #コンテンツを提供する