PowerShellにWebDAVをマウントする
最近PowerShellばっかりやっている気がするけど、今回はPowerShellにWebDAVサポートを追加した。これを使うとWebDAVサーバー上のリソースをローカルのリソースのように扱える。
ソースは以下から
マウントの仕方
PS > New-PSDrive Hoge Web http://localhost/webdav
認証が必要なWebDAVサーバーの場合
「-Login」パラメータでユーザ名を指定する。New-Driveコマンドレットには-Credentialというパラメータがあるけど、これを指定すると何故かエラーが出てしまう。現状ではまだサポートされていないのかな?
仕方がないので「-Login」という新しいパラメータを追加した。さらっと言ったけどNew-DriveやNew-ItemなどのProviderがサポートするコマンドでは、コマンドの実装を変えることなくパラメータを新しく追加することができるのだ!!
PS > New-PSDrive Hoge Web http://localhost/webdav -Login [ユーザ名]
後はローカルのファイルシステムと同じように操作が出来る。今のところサポートしている機能は、
- New-Item, ni, mkdir
- Remove-Item, rm, rmdir, del
- Copy-Item, cp, copy
- Move-Item, mv, move
- Rename-Item, ren
- Get-Item
- Get-Content
- タブ補完
ファイルの作成
これで空のファイルが作られる。
PS Hoge:\> New-Item 100.txt
「-value」を指定するとその内容をファイルに追加する。
PS Hoge:\> New-Item 100.txt -value Hello PSParentPath: PSWeb\Web::http://localhost/webdav Mode LastWriteTime Length Name ---- ------------- ------ ---- a---- 2008/02/26 15:19 5 100.txt PS Hoge:\> more 100.txt Hello
PS Hoge:\> New-Item 100.txt -source c:\Hoge\100.txt PSParentPath: PSWeb\Web::http://localhost/webdav Mode LastWriteTime Length Name ---- ------------- ------ ---- a---- 2008/02/26 15:19 5 100.txt
何種類かPowerShellのProviderを作ってわかったのは、Provider作りはだいぶクセがあること。このへんのノウハウ的なことはそのうちまとめないとな。問題は家のVAIOがぶっ壊れて、家でいっさいプログラムが書けないことか・・・
ちょっとPowerShellにも飽きてきたな。しばらく触らんとこ。
以下、全ソース
PSWebSnapIn.cs
using System; using System.Management.Automation; using System.ComponentModel; using System.Diagnostics; namespace PSWeb.Configuration { /// <summary> /// PowerShellにSnapInを追加するクラス /// </summary> [RunInstaller(true)] public class PSWebSnapIn : PSSnapIn { public PSWebSnapIn() { } public override string Description { get { return "PowerShellにWebDAVサポートを追加します。"; } } public override string Name { get { return "PSWeb"; } } public override string Vendor { get { return "coma2n"; } } } }
PSWebResource.cs
using System; using System.Diagnostics; namespace PSWeb { /// <summary> /// WebDAVリソースの情報を格納するクラス /// </summary> [Serializable] [DebuggerStepThrough] public class PSWebResource { private string name = string.Empty; private long length; private DateTime creationDate; private DateTime lastWriteTime; private bool isCollection = false; /// <summary> /// 表示名を取得します。 /// </summary> public string Name { get { return name; } } /// <summary> /// コンテンツのサイズを取得します。 /// </summary> public long Length { get { return length; } } /// <summary> /// 作成日を取得します。 /// </summary> public DateTime CreationDate { get { return creationDate; } } /// <summary> /// 最終更新日を取得します。 /// </summary> public DateTime LastWriteTime { get { return lastWriteTime; } } /// <summary> /// コレクションかどうかを取得します。 /// </summary> public bool IsCollection { get { return isCollection; } } public PSWebResource(string name, long length, DateTime creationDate, DateTime lastWriteTime) { this.name = name; this.length = length; this.creationDate = creationDate; this.lastWriteTime = lastWriteTime; } public PSWebResource(string name, long length, DateTime creationDate, DateTime lastWriteTime, bool isCollection) : this(name, length, creationDate, lastWriteTime) { this.isCollection = isCollection; } public override string ToString() { return string.Format("Name = {0}", Name); } } }
PSWebException.cs
using System; using System.Runtime.Serialization; using System.Diagnostics; namespace PSWeb { /// <summary> /// WebDAVアクセス時にエラーが発生した時に投げられる例外クラス /// </summary> [Serializable] [DebuggerStepThrough] public class PSWebException : ApplicationException { public PSWebException() { } public PSWebException(string message) : base(message) { } public PSWebException(string message, Exception innerException) : base(message, innerException) { } protected PSWebException(SerializationInfo info, StreamingContext context) : base(info, context) { } } }
PSWebRequest.cs
using System; using System.IO; using System.Net; using System.Net.Security; using System.Xml; using System.Text; using System.Collections; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Diagnostics; namespace PSWeb { /// <summary> /// WebDAVサーバーに対して各種リクエストを行うクラス /// </summary> public class PSWebRequest { private ICredentials credential; private bool useProxy = false; private string userAgent = "Mozilla/4.0"; /// <summary> /// 認証オブジェクトを取得、設定します。 /// </summary> public ICredentials Credential { get { return credential; } set { credential = value; } } /// <summary> /// プロキシを経由するかどうかを取得、設定します。 /// </summary> public bool UseProxy { get { return useProxy; } set { useProxy = value; } } /// <summary> /// ユーザーエージェントを取得、設定します。(規定ではMozilla/4.0) /// </summary> public string UserAgent { get { return userAgent; } set { userAgent = value; } } public PSWebRequest() { } /// <summary> /// 指定したuriのリソースの情報を取得します(取得に失敗するとnull)。 /// </summary> /// <param name="uri">uri</param> /// <returns>リソース情報</returns> public PSWebResource GetResource(string uri) { try { return new List<PSWebResource>(EnumResources(uri, true))[0]; } catch { return null; } } /// <summary> /// 指定したuriにあるリソースの一覧を列挙します。 /// </summary> /// <param name="uri">uri</param> /// <returns>リソースのコレクション</returns> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public IEnumerable<PSWebResource> EnumResources(string uri) { return EnumResources(uri, false); } private IEnumerable<PSWebResource> EnumResources(string uri, bool selfOnly) { // 全プロパティを取得 StringBuilder buffer = new StringBuilder(); buffer.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); buffer.Append("<D:propfind xmlns:D=\"DAV:\">"); buffer.Append("<D:allprop/>"); buffer.Append("</D:propfind>"); byte[] bytes = Encoding.UTF8.GetBytes(buffer.ToString()); HttpWebRequest httpReq = CreateWebRequest(uri, credential); // 自分以外で1階層のリソースのみを対象にする。 httpReq.Method = "PROPFIND"; httpReq.Headers.Add("Depth", selfOnly ? "0" : "1"); httpReq.ContentType = "text/xml; charset=\"utf-8\""; httpReq.ContentLength = bytes.Length; try { using(Stream request = httpReq.GetRequestStream()) { request.Write(bytes, 0, bytes.Length); } HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse(); try { return EnumResourcesInternal( Encoding.UTF8.GetString(ReadBytes(httpRes.GetResponseStream())), !selfOnly ); } finally { httpRes.Close(); } } catch(WebException exp) { throw new PSWebException("リソースの一覧取得に失敗しました", exp); } } private IEnumerable<PSWebResource> EnumResourcesInternal(string xmlString, bool skipRoot) { XmlDocument xmlDoc = new XmlDocument(); XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable); nsmgr.AddNamespace("D", "DAV:"); nsmgr.AddNamespace("lp3", "http://subversion.tigris.org/xmlns/dav/"); xmlDoc.LoadXml(xmlString); Debug.WriteLine(xmlString); bool isRootResource = skipRoot; // 1つめのノードは、自分自身なので無視する。 foreach(XmlNode node in xmlDoc.SelectNodes("//D:response", nsmgr)) { if(isRootResource) { isRootResource = false; continue; } XmlNode mn = node.SelectSingleNode(".//D:displayname", nsmgr); yield return new PSWebResource( GetDisplayName(node, nsmgr), GetContentLength(node, nsmgr), GetDate(node, ".//D:creationdate", nsmgr), GetDate(node, ".//D:getlastmodified", nsmgr), node.SelectSingleNode(".//D:resourcetype", nsmgr).ChildNodes.Count != 0 ); } } private static string GetDisplayName(XmlNode node, XmlNamespaceManager nsmgr) { string value = GetNodeString(node, ".//D:displayname", nsmgr); // Subversionの場合はこっち if(value.Length == 0) { value = GetNodeString(node, ".//lp3:baseline-relative-path", nsmgr); // 相対パスなので、分割してドンケツだけを返す。 if(value.Length > 0) { string[] values = value.Split('/'); return values[values.Length - 1]; } } return value; } private static long GetContentLength(XmlNode node, XmlNamespaceManager nsmgr) { string value = GetNodeString(node, ".//D:getcontentlength", nsmgr); if(value.Length > 0) { long result; if(long.TryParse(value, out result)) return result; } return -1; } private static DateTime GetDate(XmlNode node, string xpath, XmlNamespaceManager nsmgr) { string value = GetNodeString(node, xpath, nsmgr); if(value.Length > 0) { DateTime result; if(DateTime.TryParse(value, out result)) return result; } return DateTime.MinValue; } private static string GetNodeString(XmlNode node, string xpath, XmlNamespaceManager nsmgr) { XmlNode match = node.SelectSingleNode(xpath, nsmgr); return match != null ? match.InnerText : string.Empty; } /// <summary> /// 指定したファイルを指定したuriのリソースとして作成します。 /// </summary> /// <param name="fileName">ファイル名</param> /// <param name="uploadUri">アップロード先</param> /// <returns>作成したリソースの情報</returns> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public PSWebResource CreateResource(string fileName, string uploadUri) { byte[] bytes; using(FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { bytes = new byte[fs.Length]; fs.Read(bytes, 0, bytes.Length); } HttpWebRequest httpReq = CreateWebRequest(uploadUri, credential); httpReq.Method = "PUT"; httpReq.ContentLength = bytes.Length; try { using(Stream request = httpReq.GetRequestStream()) { request.Write(bytes, 0, bytes.Length); } using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { return GetResource(uploadUri); } } catch(WebException exp) { throw new PSWebException("リソースの作成中に問題が発生しました。", exp); } } /// <summary> /// 指定したuriのリソースを削除します。 /// </summary> /// <param name="uri">URI</param> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public void DeleteResource(string uri) { HttpWebRequest httpReq = CreateWebRequest(uri, credential); httpReq.Method = "DELETE"; try { using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { } } catch(WebException exp) { throw new PSWebException("リソースの削除中に問題が発生しました。", exp); } } /// <summary> /// 指定したuriのリソースを別のuriでコピーします。 /// </summary> /// <param name="sourceUri">コピー元</param> /// <param name="destUri">コピー先</param> /// <returns>コピーしたリソースの情報</returns> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public PSWebResource CopyResource(string sourceUri, string destUri) { HttpWebRequest httpReq = CreateWebRequest(sourceUri, credential); httpReq.Method = "COPY"; httpReq.Headers.Add("Destination", destUri); httpReq.Headers.Add("Depth", "0"); httpReq.Headers.Add("Overwrite", "T"); try { using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { return GetResource(destUri); } } catch(WebException exp) { throw new PSWebException("リソースのコピー中に問題が発生しました。", exp); } } /// <summary> /// 指定したアドレスのリソースの名前を変更します。 /// </summary> /// <param name="sourceUri">ソースURI</param> /// <param name="destUri">変更後URI</param> /// <returns>変更後のリソースの情報</returns> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public PSWebResource RenameResource(string sourceUri, string destUri) { HttpWebRequest httpReq = CreateWebRequest(sourceUri, credential); httpReq.Method = "MOVE"; httpReq.Headers.Add("Destination", destUri); httpReq.Headers.Add("Overwrite", "T"); try { using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { return GetResource(destUri); } } catch(WebException exp) { throw new PSWebException("リソースの名前変更中に問題が発生しました。", exp); } } /// <summary> /// 指定したアドレスにコレクションを作成します。 /// </summary> /// <param name="uri">アドレス</param> /// <returns>作成したコレクションの情報</returns> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public PSWebResource CreateCollection(string uri) { HttpWebRequest httpReq = CreateWebRequest(uri, credential); httpReq.Method = "MKCOL"; try { using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { return GetResource(uri); } } catch(WebException exp) { HttpWebResponse response = (HttpWebResponse)exp.Response; // 405エラーは、コレクションが既に存在する場合と解釈する。 if(response != null && response.StatusCode == HttpStatusCode.MethodNotAllowed) return GetResource(uri); throw new PSWebException("コレクションの作成中に問題が発生しました。", exp); } } /// <summary> /// 指定したアドレスのリソースを指定したファイル名でダウンロードします。 /// </summary> /// <param name="uri">アドレス</param> /// <param name="fileName">ローカルのファイル名</param> /// <exception cref="PSWebException">WebDavアクセス中に問題が発生した時</exception> public void DownloadResource(string uri, string fileName) { HttpWebRequest httpReq = CreateWebRequest(uri, credential); httpReq.Method = "GET"; try { using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { byte[] buffer = ReadBytes(httpRes.GetResponseStream()); using(FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { fs.Write(buffer, 0, buffer.Length); } } } catch(WebException exp) { throw new PSWebException("リソースの取得中に問題が発生しました。", exp); } } /// <summary> /// 指定したリソースが存在するかどうかを調べます。 /// </summary> /// <param name="uri">アドレス</param> /// <returns>存在すればtrue、そうでなければfalse</returns> public bool ExistResource(string uri) { HttpWebRequest httpReq = CreateWebRequest(uri, credential); httpReq.Method = "HEAD"; try { using(HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse()) { } return true; } catch(WebException exp) { // NotFoundでなければOKじゃね? HttpWebResponse response = (HttpWebResponse)exp.Response; if(response != null && response.StatusCode != HttpStatusCode.NotFound) return true; return false; } } /// <summary> /// 指定した(シーク不可の)ストリームからbyte配列に読み込みます。 /// </summary> /// <param name="inStream">ストリーム</param> /// <returns>byte配列</returns> private byte[] ReadBytes(Stream inStream) { List<byte> buffer = new List<byte>(); int bt; while((bt = inStream.ReadByte()) != -1) { buffer.Add((byte)bt); } inStream.Close(); return buffer.ToArray(); } /// <summary> /// 指定したURIと認証オブジェクトがWebリクエストを作成します。 /// </summary> /// <param name="uri">URI</param> /// <param name="credential">認証オブジェクト</param> /// <returns>Webリクエスト</returns> private HttpWebRequest CreateWebRequest(string uri, ICredentials credential) { HttpWebRequest httpReq = (HttpWebRequest)HttpWebRequest.Create(uri); httpReq.UserAgent = UserAgent; // プロキシを経由すると更新系の処理でエラーが出る。 if(!useProxy) httpReq.Proxy = null; if(credential != null) { httpReq.PreAuthenticate = true; // 事前認証あり httpReq.Credentials = credential; } InitCertificateValidation(); return httpReq; } /// <summary> /// 証明書の承認処理を初期化します。 /// </summary> private void InitCertificateValidation() { // SSL証明書をオールOKにする。(セキュリティがやばい?) ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback( delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } ); } } }
PSWebProvider.cs
using System; using System.IO; using System.Text; using System.Security; using System.Management.Automation; using System.Management.Automation.Provider; using System.Collections.ObjectModel; using System.Diagnostics; namespace PSWeb { /// <summary> /// WebDAVプロトコルへの操作を提供するプロバイダクラス /// </summary> [CmdletProvider("Web", ProviderCapabilities.ShouldProcess)] public class PSWebProvider : NavigationCmdletProvider, IContentCmdletProvider { /// <summary> /// パスの区切り文字 /// </summary> private const string PATH_SEPARATOR = "/"; /// <summary> /// New-Driveコマンドレットのパラメータを定義するクラス /// </summary> [Serializable] [DebuggerStepThrough] public class NewDriveParameters { private SwitchParameter useProxy; private PSCredential credential; /// <summary> /// プロキシを使用するかどうかを取得、設定します。 /// </summary> [Parameter] public SwitchParameter UseProxy { get { return useProxy; } set { useProxy = value; } } /// <summary> /// 認証情報を取得、設定します。 /// </summary> [Parameter, Credential] public PSCredential Login { get { return credential; } set { credential = value; } } } /// <summary> /// New-Itemコマンドレットのパラメータを定義するクラス /// </summary> [Serializable] [DebuggerStepThrough] public class NewItemParameters { private string source; /// <summary> /// 作成元のファイルを取得、設定します。 /// </summary> [Parameter] public string Source { get { return source; } set { source = value; } } } /// <summary> /// Webドライブの情報を格納するクラス /// </summary> private class PSWebDriveInfo : PSDriveInfo { private SwitchParameter useProxy; /// <summary> /// プロキシを使用するかどうかを取得します。 /// </summary> public SwitchParameter UseProxy { get { return useProxy; } } /// <summary> /// 元になるドライブ情報とNew-Driveコマンドのパラメータ情報を設定するコンストラクタ /// </summary> /// <param name="drive">ドライブ情報</param> /// <param name="ndParams">パラメータ情報</param> public PSWebDriveInfo(PSDriveInfo drive, NewDriveParameters ndParams) : base(drive.Name, drive.Provider, drive.Root, drive.Description, ndParams.Login) { this.useProxy = ndParams.UseProxy; } } public PSWebProvider() { } /// <summary> /// Webリクエストを取得します。 /// </summary> /// <returns>Webリクエスト</returns> private PSWebRequest GetWebRequest() { PSWebDriveInfo drive = (PSWebDriveInfo)base.PSDriveInfo; PSWebRequest webReq = new PSWebRequest(); if(drive != null) { if(drive.Credential.UserName != null) { webReq.Credential = drive.Credential.GetNetworkCredential(); } webReq.UseProxy = drive.UseProxy; } return webReq; } /// <summary> /// 指定したパスのアイテムの一覧をパイプラインに出力します。 /// </summary> /// <param name="path">パス</param> /// <param name="recurse">サブフォルダも再帰的に走査するかどうか</param> /// <param name="nameOnly">名前だけ出力するかどうか</param> private void GetPathItems(string path, bool recurse, bool nameOnly) { foreach(PSWebResource o in GetWebRequest().EnumResources(path)) { if(base.Stopping) return; WriteItemObject( (nameOnly ? (object)o.Name : WrapPSObject(o, path)), Combine(path, o.Name), o.IsCollection ); if(o.IsCollection && recurse) { GetPathItems(Combine(path, o.Name), true, nameOnly); } } } /// <summary> /// 指定したリソースオブジェクトをパイプラインに書き込みます。 /// </summary> /// <param name="webRes">オブジェクト</param> /// <param name="path">パス</param> private void WritePSObject(PSWebResource webRes, string path) { WriteItemObject( WrapPSObject( webRes, GetParentPath(path, base.PSDriveInfo.Root) ), path, webRes.IsCollection ); } /// <summary> /// 指定したオブジェクトをPSObjectにラップして返します。 /// </summary> /// <param name="obj">オブジェクト</param> /// <param name="parentPath">親のパス</param> /// <returns>ラップしたオブジェクト</returns> private static PSObject WrapPSObject(object obj, string parentPath) { PSObject psObj = PSObject.AsPSObject(obj); psObj.Properties.Add( new PSNoteProperty("PSParentPath", parentPath) ); return psObj; } /// <summary> /// 指定した二つのパスを連結します。 /// </summary> /// <param name="path1">パス1</param> /// <param name="path2">パス2</param> /// <returns>「/」で連結したパス</returns> private static string Combine(string path1, string path2) { if(path1.Length > 0) { return string.Format( "{0}{2}{1}", path1, path2, (path1.EndsWith(PATH_SEPARATOR) ? string.Empty : PATH_SEPARATOR) ); } else { return path2; } } protected override PSDriveInfo NewDrive(PSDriveInfo drive) { NewDriveParameters ndParams = (NewDriveParameters)base.DynamicParameters; return new PSWebDriveInfo(drive, ndParams); } protected override object NewDriveDynamicParameters() { return new NewDriveParameters(); } protected override void NewItem(string path, string itemTypeName, object newItemValue) { if(!string.IsNullOrEmpty(itemTypeName) && itemTypeName.Equals("Directory", StringComparison.CurrentCultureIgnoreCase)) { WritePSObject( GetWebRequest().CreateCollection(path), path ); } else { NewItemParameters niParams = (NewItemParameters)base.DynamicParameters; // パラメータで作成元を指定された場合はそれを使うが、 // 指定されなければ空の一時ファイルを作成して、それをアップロードする。 string sourceFile = string.IsNullOrEmpty(niParams.Source) ? Path.GetTempFileName() : Path.GetFullPath(niParams.Source); // 値が指定されている場合はファイルに追加書き込みする。 if(newItemValue != null) { using(StreamWriter sw = new StreamWriter(sourceFile, true)) { sw.Write(newItemValue.ToString()); } } WritePSObject( GetWebRequest().CreateResource(sourceFile, path), path ); } } protected override object NewItemDynamicParameters(string path, string itemTypeName, object newItemValue) { return new NewItemParameters(); } protected override void CopyItem(string path, string copyPath, bool recurse) { WritePSObject( GetWebRequest().CopyResource(path, copyPath), copyPath ); } protected override void RemoveItem(string path, bool recurse) { GetWebRequest().DeleteResource(path); } protected override void RenameItem(string path, string newName) { string destination = Combine( GetParentPath(path, base.PSDriveInfo.Root), newName ); WritePSObject( GetWebRequest().RenameResource(path, destination), destination ); } protected override void MoveItem(string path, string destination) { PSWebResource mvRes = GetWebRequest().RenameResource(path, destination); WritePSObject( GetWebRequest().RenameResource(path, destination), destination ); } protected override void GetItem(string path) { WritePSObject( GetWebRequest().GetResource(path), path ); } protected override string GetParentPath(string path, string root) { return base.GetParentPath(path, root).Replace("\\", PATH_SEPARATOR); } protected override void GetChildItems(string path, bool recurse) { GetPathItems(path, recurse, false); } protected override void GetChildNames(string path, ReturnContainers returnContainers) { GetPathItems(path, false, true); } protected override bool IsItemContainer(string path) { // 指定したパスがコンテナ(cdできる)かどうか return true; } protected override bool HasChildItems(string path) { // そんなの関係ねぇ!! return false; } protected override bool ItemExists(string path) { return GetWebRequest().ExistResource(path); } protected override string MakePath(string parent, string child) { if(child.Length == 0) return parent; if(parent.Length == 0) return child; return Combine(parent, child); } protected override string NormalizeRelativePath(string path, string basePath) { return base.NormalizeRelativePath(path, basePath).Replace(PATH_SEPARATOR, "\\"); } protected override bool IsValidPath(string path) { // 指定したパスが有効かどうか return true; } public void ClearContent(string path) { } public object ClearContentDynamicParameters(string path) { return null; } public IContentReader GetContentReader(string path) { return new PSWebContentReader( GetWebRequest().GetResourceStream(path), Encoding.Default ); } public object GetContentReaderDynamicParameters(string path) { return null; } public IContentWriter GetContentWriter(string path) { return null; } public object GetContentWriterDynamicParameters(string path) { return null; } } }
PSWebContentReader.cs
using System; using System.IO; using System.Text; using System.Collections; using System.Collections.Generic; using System.Management.Automation.Provider; using System.Diagnostics; namespace PSWeb { /// <summary> /// WebDAVリソースのコンテンツを読み込むクラス /// </summary> public class PSWebContentReader : IContentReader { private Stream stream; private StreamReader reader; /// <summary> /// 読み取るストリームを設定するコンストラクタ /// </summary> /// <param name="stream">ストリーム</param> /// <param name="encoding">エンコード</param> public PSWebContentReader(Stream stream, Encoding encoding) { this.stream = stream; this.reader = new StreamReader(stream, encoding); } public void Close() { reader.Close(); stream.Close(); } public IList Read(long readCount) { List<string> lines = new List<string>(); for(int i = 0; i < readCount; i++) { string line = reader.ReadLine(); if(line == null) break; lines.Add(line); } return lines.Count > 0 ? lines : null; } public void Seek(long offset, SeekOrigin origin) { stream.Seek(offset, origin); } public void Dispose() { if(reader != null) { reader.Dispose(); reader = null; } if(stream != null) { stream.Dispose(); stream = null; } } } }
psweb.format.ps1xml
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <ViewDefinitions> <View> <Name>PSWeb.PSWebResource</Name> <ViewSelectedBy> <TypeName>PSWeb.PSWebResource</TypeName> </ViewSelectedBy> <GroupBy> <PropertyName>PSParentPath</PropertyName> </GroupBy> <TableControl> <TableHeaders> <TableColumnHeader> <Label>Mode</Label> <Width>7</Width> </TableColumnHeader> <TableColumnHeader> <Label>LastWriteTime</Label> <Alignment>Right</Alignment> <Width>25</Width> </TableColumnHeader> <TableColumnHeader> <Label>Length</Label> <Alignment>Right</Alignment> <Width>10</Width> </TableColumnHeader> <TableColumnHeader /> </TableHeaders> <TableRowEntries> <TableRowEntry> <TableColumnItems> <TableColumnItem> <ScriptBlock> $catr = ""; if($_.IsCollection) { $catr += "d-" } else { $catr += "a-" } $catr += "---" $catr </ScriptBlock> </TableColumnItem> <TableColumnItem> <ScriptBlock> [String]::Format("{0,10} {1,8}", $_.LastWriteTime.ToString("d"), $_.LastWriteTime.ToString("t")) </ScriptBlock> </TableColumnItem> <TableColumnItem> <ScriptBlock>if(!$_.IsCollection) { $_.Length }</ScriptBlock> </TableColumnItem> <TableColumnItem> <PropertyName>Name</PropertyName> </TableColumnItem> </TableColumnItems> </TableRowEntry> </TableRowEntries> </TableControl> </View> </ViewDefinitions> </Configuration>