使用Frontpage RPC管理web站点的文件

往往我们需要对一个 web 站点的文件进行管理,而常用两种方式:第一,通过 ftp 协议进行文件管理;第二,通过 web 页面提供的该功能( yahoo 的公文包)。而这里将介绍第三种方式: FrontPage 服务器扩展( FrontPage Server Extensions Remote Procedure Call )。使用 FrontPage 服务器扩展有以下优点:

l 使创作者们能协作创建和维护 web 站点、直接在服务器计算机上编辑一个 Web 站点(节省下载时间),并且在不必编写程序的情况下,轻易地在站点上加入新功能。

l 支持站点计数器、全文搜索、电子邮件表单处理程序和其他由创作者使用 FrontPage 添加到站点的功能。您不必下载、购买或安装独立的 CGI 兼容的程序来实现上述功能。

l 能在许多流行的服务器平台,如 Windows NT 和 UNIX ,以及许多流行的站点服务器,如 Microsoft Internet Information Services (IIS) 、 Apache 、 WebSite 和 Netscape 上工作。

l 在一个 Web 站点中移动、删除或重命名一个网页(只是网页的文件名,而不是必须传送到站点服务器的整个文件的名称)后,自动更新超链接。

l 支持与 Microsoft Office 、 Visual SourceSafe 和 Index Server 的集成。

Frontpage RPC 通过 Frontpage RPC 方法 来控制 web 站点,实现以上的功能。这里只是介绍如何通过 Frontpage RPC 对 web 站点的文件进行管理。对大家起一个抛砖引玉的作用。 Frontpage RPC 的是基于 http 协议的一种远程过程调用。 Frontpage RPC 通过 http POST 命令向 web 站点中特定的 dll 发送方法,而 web 服务器将返回带有操作结果信息 HTML 文档到客户端。客户端可通过该文档判断是否操作成功。

Frontpage RPC 方法位于“ method= ”后,方法名后面有一个冒号“:”,它是方法名与 Frontpage 服务器扩展的版本号的分隔符。版本号后面跟着该方法的参数。每个参数以记号“ & ”作为第一个字母,参数名紧跟“ & ”后面。参数名与参数的值用“ = ”连接,中间不能由任何的空格符号。可以参见下面的例子:

method=get document: _server_extension_version_


&service_name=/&document_name= _service_relative_path_ / _file_name_


[&dir_name= _directory_name_ ][&effective_protocol_version= _server_extension_version_ ]


&old_theme_html=(true|false)&force=(true|false)[&doc_version= _string_ ]


 &get_option=(none|0|1|2)&timeout= _time_in_seconds_ [&validateWelcomeNames=(true|false)]


 

** 字体 / 代码 **

|

** 意义 **

|

** 例子 **

---|---|---

斜体

|

变量(参数)的值

|

_ service_relative_path _ / file_name

方括号( [ 、 ] )

|

可选的内容

|

[&validateWelcomeNames=(true|false)]

无格式文本

|

字面意义

|

method

分隔符( | )

|

分开可选的选项

|

(true|false)

接下来看看在这里将会使用到的 Frontpage RPC 方法 ,具体的参数可以参看( http://msdn.microsoft.com/library/en-us/spptsdk/html/SPPTWSSFPSERPC_SV01072918.asp?frame=true ):

l get document 获取指定的文档。

l list documents 列出位于指定 url 的 web 站点下文件、文件夹和子站点以及他们的 meta-info 。

l move document 修改指定文件的文件名、移动指定的文件到指定的文件夹、复制指定文件。

l put document 上传一个文件或文件夹到已存在的 web 站点。

l remove documents 删除 wen 站点上指定的文件或文件夹。

在编码的过程中应该注意一下几点:

1. url 编码问题:

l 在 http 协议中发送的数据主体必须是 8 位编码的字符,也就是只能由 0-225 之间的字符组成。中文字符是不能出现的。所以在所有 Frontpage RPC 中方法中所有的参数都需要经过 url 编码。 url 编码方式是把字符相对应的二进制的值转换为十进制,然后再前面加“ % ”。 url 中文的编码方式先将中文编码成对应的字节,然后在在前面加上“ % ”。比如“中国”使用 UTF-8 的编码是“ 228 ”,“ 184 ”,“ 173 ”,“ 229 ”,“ 155 ”,“ 189 ”六个字节组成,那么 url 的对应得编码应该是“ %228%184%173%229%155%189 ”。如果“中国”使用 gb2312 的编码是“ 214 ”,“ 208 ”,“ 185 ”,“ 250 ”四个字节,那么对应的 url 编码应该是“ %214%208%185%250 ”

l Frontpage RPC 中方法中所有的参数不能出现除数字和字母以外的字符,例如一般可以在地址栏看到的符号“ . ”、“ _ ”都不允许出现在参数中。 C# 提供的 UrlEncode 方法不会对“ . ”、“ _ ”这两个字符进行编码,所以我们需要写一个函数进行编码。

l 文件的 url 中如果带有“ ; ”、“ = ”、“ [ ”、“ ] ”都需要转义为“ \ ; ”、“ \ = ”、“ \ [ ”、“ \ ] ”才进行 url 编码。否则会提示找不到该文件。

2. html 编码问题: html 的编码与 url 相类似,只是 html 编码会将 UTF-8 “中国”编码为:“ ä&# 184;&# 229;&# 155;&# 155;&# 189; ”, gb2312 编码为“ Ö&# 208;&# 185;&# 250; ”。

3. 注意方法中的大小写。这个问题我是在写这篇文章的时候,做演示工程的时候发现的问题。因为实际使用中可能是版本的问题,大小写与 sdk 中的有些出入。例如 oldUrl 正确的是参数名应该是 oldUrl ,而微软官方文档中的是 oldURL 。

4. Web 站点和子站点的关系。曾经在开发的过程中,忽略的这个问题最后的直接后果是导致加班到晚上 1 点。在操作不同的子站点的时候, Frontpage RPC 方法发送的 dll 的 url 也是不一样的。而且子站点与子站点是独立的。不能够进行类似将子站点 A 的文件复制到子站点 B 这样的操作。

最后还是看看一些关键的代码:

using System;
using System.IO;
using System.Net;
using System.Web;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.Threading;
using System.Net.Sockets;

namespace FPSERPCClient
{
/// <summary>
/// Summary description for FPSEPublish.
/// </summary>
public class FPSERPCClient
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="username"> 用于登陆站点的用户名 </param>
/// <param name="password"> 用于登陆站点的密码 </param>
public FPSERPCClient( string username, string password)
{
Credential = new NetworkCredential(username, password);
}
/// <summary>
/// 删除 web 站点上指定的文件或文件夹。
/// </summary>
/// <param name="uri"> 删除文件的 url </param>
/// <returns></returns>
public string RemoveDocument( string uri)
{
Uri myUri = new Uri(uri, true );
string webUrl, fileUrl;
UrlToWebUrl(uri, out webUrl, out fileUrl);
/// 这是最简单的一个方法
string postBody = String.Format("
method=remove documents&service_name={0}&url_list=[{1}]",
UrlEncode(webUrl),
UrlEncode(fileUrl));
return SendRequest(myUri.GetLeftPart(UriPartial.Authority) +
webUrl.TrimEnd('/') + "/_vti_bin/_vti_aut/author.dll", postBody);
}

/// <summary>
/// 发送 Frontpage RPC 方法的请求重载
/// </summary>
/// <param name="uri"> 发送请求的目标 Url </param>
/// <param name="postBody"> 发送请求的主体 </param>
/// <returns></returns>
private string SendRequest( string uri, string postBody)
{
return SendRequest(uri, postBody, null );
}

/// <summary>
/// 发送 Frontpage RPC 方法的请求重载
/// </summary>
/// <param name="uri"> 发送请求的目标 Url </param>
/// <param name="postBody"> 发送请求的主体 </param>
/// <param name="fileStream"> 下载文件保存的 FileStream </param>
/// <returns></returns>
private string SendRequest( string uri, string postBody, Stream fileStream)
{
byte [] bPostBody = BodyEncoding.GetBytes(postBody);
return SendRequest(uri, bPostBody, bPostBody.Length, fileStream);
}

/// <summary>
/// 发送 Frontpage RPC 方法的请求重载
/// </summary>
/// <param name="uri"> 发送请求的目标 Url </param>
/// <param name="postBody"> 发送请求的主体 </param>
/// <param name="postLength"> 发送请求的主体长度 </param>
/// <param name="fileStream"> 下载文件保存的 FileStream </param>
/// <returns> 返回接受的 Response, 为 html 格式的字符串 </returns>
private string SendRequest( string uri, byte [] postBody, long postLength, Stream fileStream)
{
string strResponseText = null ;
Stream sendStream = null ;
HttpWebResponse response = null ;
Stream receiveStream = null ;
StreamReader responseTestReader = null ;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
request.Headers.Add("X-Vermeer-Content-Type", "application/x-www-form-urlencoded");
request.UserAgent = "DRSERPCClient";
request.Accept = "auth/sicily";
request.Expect = "200-ok";
request.KeepAlive = true ;
request.Credentials = this .Credential;
request.Timeout = HTTPREQUEST_TIMEOUT;
request.PreAuthenticate= true ; // let's stop sending bunks of data prior to detecting 401

sendStream = request.GetRequestStream();

int iOffset = 0;
while (postLength > 0)
{
if (postLength > 4096)
{
sendStream.Write(postBody, iOffset, 4096);
postLength -= 4096;
iOffset += 4096;
}
else
{
sendStream.Write(postBody, iOffset, Convert.ToInt32(postLength));
break ;
}
}

sendStream.Close();

response = (HttpWebResponse)request.GetResponse();
receiveStream = response.GetResponseStream();

int readBytes = 0;
byte [] readBuffer = new byte [BUFFER_SIZE];

/// 接受到的 Response 以“ </HTML> \n”为分割符,后面的为下载文件的主体部分
while ((readBytes = receiveStream.Read(readBuffer,0, readBuffer.Length)) != 0)
{
strResponseText += BodyEncoding.GetString(readBuffer, 0, readBytes);
int separatorOffset = strResponseText.ToUpper().IndexOf("</HTML>") + 8;
if (separatorOffset != 7)
{

strResponseText = strResponseText.Substring(0, separatorOffset);
if (fileStream != null )
{
fileStream.Write(readBuffer, separatorOffset % BUFFER_SIZE , readBytes - separatorOffset);
}
break ;
}
}
/// 把下载文件的内容写入文件流中,最后保存在磁盘上
if (fileStream != null )
{
while ((readBytes = receiveStream.Read(readBuffer,0, readBuffer.Length)) != 0)
{
fileStream.Write(readBuffer, 0, readBytes);
}
}

receiveStream.Close();
response.Close();

}
catch (Exception e)
{
if (sendStream != null )
sendStream.Close();
if (responseTestReader != null )
responseTestReader.Close();
if (receiveStream != null )
receiveStream.Close();
if (response != null )
response.Close();
throw e;
}

return strResponseText;
}

/// <summary>
/// 获取一个绝对 Url 的字站点地址,和相对该子站点的文件地址
/// </summary>
/// <param name="uri"> 绝对 URL </param>
/// <param name="webUrl"> 子站点地址 </param>
/// <param name="fileUrl"> 相对子站点的文件地址 </param>
private void UrlToWebUrl( string uri, out string webUrl, out string fileUrl)
{
Uri myUri = new Uri(uri, true );
string strUriPartial = string .Empty;
for ( int i = 0;i < myUri.Segments.Length; i++ )
strUriPartial += myUri.Segments[i];

string postBody = String.Format("method=url+to+web+url&url={0}&flags=0", UrlEncode(strUriPartial));
string response = SendRequest(myUri.GetLeftPart(UriPartial.Authority) + "/_vti_bin/shtml.dll/_vti_rpc",postBody);
webUrl = GetReturnValue(response, "webUrl");
fileUrl = GetReturnValue(response, "fileUrl");
}

/// <summary>
/// 获取一个返回的 html 中特定的值
/// </summary>
/// <param name="responseText"> web 站点返回的 html 字符串 </param>
/// <param name="key"> 需要获取值得 key </param>
/// <returns> 返回该 key 的值 </returns>
public string GetReturnValue( string responseText, string key)
{
int start = responseText.IndexOf(key + "=");
if (-1 == start)
return null ;
else
start += key.Length + 1;

int end = responseText.IndexOf("\n", start);
return HtmlDecode(responseText.Substring(start, end - start));
}

/// <summary>
/// 获取一组返回的 html 中特定的值
/// </summary>
/// <param name="responseText"> web 站点返回的 html 字符串 </param>
/// <param name="key"> 需要获取值得 key </param>
/// <returns> 返回该 key 的值 </returns>
public string [] GetReturnValues( string responseText, string key)
{
int start = 0;
int end = 0;
string returnValue = string .Empty;
ArrayList returnValuesList = new ArrayList();
while ( true )
{
start = responseText.IndexOf(key + "=", end) + key.Length + 1;
if (start == key.Length)
break ;
end = responseText.IndexOf("\n", start);
returnValue = responseText.Substring(start, end - start);
returnValuesList.Add(HtmlDecode(returnValue));
}
string [] returnValues = new string [returnValuesList.Count];
returnValuesList.CopyTo(returnValues);
return returnValues;
}
/// <summary>
/// url 编码
/// </summary>
/// <param name="url"> 需进行编码的 URL </param>
/// <param name="e"> 进行编码的类 </param>
/// <returns> 返回编码以后的值 </returns>
public string UrlEncode( string url, Encoding e)
{
url = url.Replace(";", "\;");
url = url.Replace("=", "\=");
url = url.Replace("[", "\\[");
url = url.Replace("]", "\\]");
string encodedUrl = HttpUtility.UrlEncode(url, e);
encodedUrl = encodedUrl.Replace(".", "%2e");
encodedUrl = encodedUrl.Replace("_", "% 5f");
return encodedUrl;
}
/// <summary>
/// url 编码
/// </summary>
/// <param name="url"> 需进行编码的 URL </param>
/// <returns> 返回编码以后的值 </returns>
public string UrlEncode( string url)
{
return this .UrlEncode(url, UrlEncoding);
}
/// <summary>
/// html 编码
/// </summary>
/// </sp


Published At
Categories with Web编程
Tagged with
comments powered by Disqus