C#編寫的一個反向代理工具,可以緩存網頁到本地

enpc 9年前發布 | 3K 次閱讀 C#

C#編寫的一個反向代理工具,可以緩存網頁到本地
proxy.ashx 主文件

<%@ WebHandler Language="C#" Class="proxy" %>

using System; using System.Web; using System.Net; using System.Text; using System.IO; using System.Collections.Generic; using System.Configuration;

/// <summary> /// 把http headers 和 http 響應的內容 分別存儲在 /proxy/header/ 和 /proxy/body/ 中 /// 分層次創建目錄 /// </summary> public class proxy : IHttpHandler {

HttpResponse Response;
HttpRequest Request;
HttpApplicationState Application;
HttpServerUtility Server;

static string proxyCacheFolder = ConfigurationManager.AppSettings["proxyCacheFolder"];
static string proxyDomain = ConfigurationManager.AppSettings["proxyDomain"];
static string proxyReferer = ConfigurationManager.AppSettings["proxyReferer"];
bool proxyCacheDirectAccess = ConfigurationManager.AppSettings["proxyCacheDirectAccess"] == "true";
int proxyCacheSeconds = int.Parse(ConfigurationManager.AppSettings["proxyCacheSeconds"]);



public void ProcessRequest(HttpContext context)
{
    Response = context.Response;
    Request = context.Request;
    Application = context.Application;
    Server = context.Server;

    string path = context.Request.RawUrl;
    bool delCache = path.IndexOf("?del") > 0;
    if (delCache)
    {
        path = path.Replace("?del", string.Empty);
        DeleteCacheFile(path);
        return;
    }


    bool allowCache = Request.QueryString["cache"] == "true";
    string seconds = Request.QueryString["seconds"] ?? string.Empty;
    if (!int.TryParse(seconds, out proxyCacheSeconds))
    {
        proxyCacheSeconds = 3600;
    }

    if (allowCache)
    {

        EchoData(path);
    }
    else
    {

        WebClient wc = new WebClient();
        wc.Headers.Set("Referer", proxyReferer);
        byte[] buffer = wc.DownloadData(proxyDomain + path);
        Response.ContentType = wc.ResponseHeaders["Content-Type"];
        foreach (string key in wc.ResponseHeaders.AllKeys)
        {

            Response.Headers.Set(key, wc.ResponseHeaders[key]);
        }
        wc.Dispose();
        Response.OutputStream.Write(buffer, 0, buffer.Length);
    }



}


/// <summary>
/// 清理失效的緩存
/// </summary>
/// <param name="d"></param>
void ClearTimeoutCache(DirectoryInfo d)
{
    if (d.Exists)
    {
        FileInfo[] files = d.GetFiles();
        foreach (FileInfo file in files)
        {
            TimeSpan timeSpan = DateTime.Now - file.LastAccessTime;
            if (timeSpan.TotalSeconds > proxyCacheSeconds)
            {
                file.Delete();
            }
        }
    }
}

string GetCacheFolderPath(string hash)
{
    string s = string.Empty;
    for (int i = 0; i <= 2; i++)
    {
        s += hash[i] + "/";
    }

    return s;
}

/// <summary>
/// 讀取緩存的header 并輸出
/// </summary>
/// <param name="cacheHeaderPath"></param>
void EchoCacheHeader(string cacheHeaderPath)
{
    string[] headers = File.ReadAllLines(cacheHeaderPath);
    for (int i = 0; i < headers.Length; i++)
    {
        string[] headerKeyValue = headers[i].Split(':');
        if (headerKeyValue.Length == 2)
        {
            if (headerKeyValue[0] == "Content-Type")
            {
                Response.ContentType = headerKeyValue[1];
            }
            Response.Headers.Set(headerKeyValue[0], headerKeyValue[1]);
        }

    }

}



void DeleteCacheFile(string path)
{
    string absFolder = Server.MapPath(proxyCacheFolder);
    string hash = GetHashString(path);

    string folder = GetCacheFolderPath(hash);

    string cacheBodyPath = absFolder + "/body/" + folder + hash;
    string cacheHeaderPath = absFolder + "/header/" + folder + hash;

    FileInfo cacheBody = new FileInfo(cacheBodyPath);
    FileInfo cacheHeader = new FileInfo(cacheHeaderPath);

    if (cacheBody.Exists)
    {
        cacheBody.Delete();
    }

    if (cacheHeader.Exists)
    {
        cacheHeader.Delete();
    }

    Response.Write("delete cache file Success!\r\n" + path);
}

/// <summary>
/// 輸出緩存
/// </summary>
/// <param name="cacheHeaderPath">緩存header 的文件路徑</param>
/// <param name="cacheBodyPath">緩存 body 的文件路徑</param>
/// <param name="ifTimeout">是否進行判斷文件過期</param>
/// <returns>是否輸出成功</returns>
bool EchoCacheFile(string cacheHeaderPath, string cacheBodyPath, bool ifTimeout)
{
    FileInfo cacheBody = new FileInfo(cacheBodyPath);
    FileInfo cacheHeader = new FileInfo(cacheHeaderPath);

    ClearTimeoutCache(cacheBody.Directory);
    ClearTimeoutCache(cacheHeader.Directory);

    if (cacheBody.Exists && cacheHeader.Exists)
    {

        if (ifTimeout)
        {
            TimeSpan timeSpan = DateTime.Now - cacheBody.LastWriteTime;
            if (timeSpan.TotalSeconds < proxyCacheSeconds)
            {
                EchoCacheHeader(cacheHeaderPath);

                Response.TransmitFile(cacheBodyPath);
                return true;
            }
        }
        else
        {
            EchoCacheHeader(cacheHeaderPath);

            Response.TransmitFile(cacheBodyPath);
            return true;
        }

    }

    return false;
}

void EchoData(string path)
{

    string absFolder = Server.MapPath(proxyCacheFolder);
    string hash = GetHashString(path);

    string folder = GetCacheFolderPath(hash);

    string cacheBodyPath = absFolder + "/body/" + folder + hash;
    string cacheHeaderPath = absFolder + "/header/" + folder + hash;

    bool success;
    if (proxyCacheDirectAccess)
    {
        success = EchoCacheFile(cacheHeaderPath, cacheBodyPath, false);
        if (!success)
        {
            Response.Write("直接從緩存讀取失敗!");
        }
        return;
    }

    success = EchoCacheFile(cacheHeaderPath, cacheBodyPath, true);

    if (success)
    {
        return;
    }




    //更新Cache File
    string ApplicationKey = "CacheList";
    List<string> List = null;


    if (Application[ApplicationKey] == null)
    {           
        Application.Lock();
        Application[ApplicationKey] = List = new List<string>(1000);
        Application.UnLock();
    }
    else
    {

        List = (List<string>)Application[ApplicationKey];

    }


    //判斷是否已有另一個進程正在更新Cache File
    if (List.Contains(hash))
    {
        success = EchoCacheFile(cacheHeaderPath, cacheBodyPath, false);
        if (success)
        {
            return;
        }
        else
        {

            WebClient wc = new WebClient();
            wc.Headers.Set("Referer", proxyReferer);
            //主體內容
            byte[] data = wc.DownloadData(proxyDomain + path);

            //處理header
            Response.ContentType = wc.ResponseHeaders["Content-Type"];
            foreach (string key in wc.ResponseHeaders.AllKeys)
            {
                Response.Headers.Set(key, wc.ResponseHeaders[key]);
            }
            wc.Dispose();
            Response.BinaryWrite(data);

        }
    }
    else
    {




        WebClient wc = new WebClient();
        wc.Headers.Set("Referer", proxyReferer);
        StringBuilder headersb = new StringBuilder();

        List.Add(hash);
        //主體內容
        byte[] data = wc.DownloadData(proxyDomain + path);





        //處理header

        Response.ContentType = wc.ResponseHeaders["Content-Type"];
        foreach (string key in wc.ResponseHeaders.AllKeys)
        {
            headersb.Append(key);
            headersb.Append(":");
            headersb.Append(wc.ResponseHeaders[key]);
            headersb.Append("\r\n");
            Response.Headers.Set(key, wc.ResponseHeaders[key]);
        }
        wc.Dispose();

        string headers = headersb.ToString().Trim();
        if (!Directory.Exists(absFolder + "/header/" + folder))
        {
            Directory.CreateDirectory(absFolder + "/header/" + folder);
        }

        StreamWriter sw = File.CreateText(absFolder + "/header/" + folder + hash);
        sw.Write(headers);
        sw.Close();
        sw.Dispose();


        //處理緩存內容
        if (!Directory.Exists(absFolder + "/body/" + folder))
        {
            Directory.CreateDirectory(absFolder + "/body/" + folder);
        }

        FileStream fs = File.Create(absFolder + "/body/" + folder + hash);
        fs.Write(data, 0, data.Length);
        fs.Close();
        fs.Dispose();

        List.Remove(hash);



        Response.BinaryWrite(data);
    }
}




string GetHashString(string path)
{
    string md5 = GetMd5Str(path);
    return md5;
}


static string GetMd5Str(string ConvertString)
{
    System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
    string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8);
    t2 = t2.Replace("-", "");

    return t2;
}


public bool IsReusable
{
    get
    {
        return false;
    }
}

} </pre>
web.config

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="RewriterConfig" type="URLRewriter.Config.RewriterConfigSerializerSectionHandler, URLRewriter"/>
</configSections> <RewriterConfig> <Rules>

  <RewriterRule>
    <LookFor>~/.*$</LookFor>
    <SendTo>
    <!--cache=true 設置此路徑進行緩存-->
      <![CDATA[~/proxy.ashx?cache=true&seconds=30]]>
    </SendTo>
  </RewriterRule>

  <RewriterRule>
    <LookFor>~/ajax/.*$</LookFor>
    <SendTo>
    <!--cache=false 設置此路徑不允許緩存-->
      <![CDATA[~/proxy.ashx?cache=false]]>
    </SendTo>
  </RewriterRule>

</Rules>

</RewriterConfig>

<appSettings>

<!--#反向代理設置 start-->

<!--設置站點-->
<add key="proxyDomain" value="http://127.0.0.1:12123/"/>

<!--緩存文件夾-->
<add key="proxyCacheFolder" value="/proxyCache/"/>

<!--緩存時長-->
<add key="proxyCacheSeconds" value="3600"/>

<!--設置不再判斷緩存文件是否超時,直接從緩存讀取-->
<add key="proxyCacheDirectAccess" value="false"/>

<!--設置反向代理Referer-->
<add key="proxyReferer" value="http://www.www.com/"/>
<!--#反向代理設置 end-->


</appSettings>

<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add type="URLRewriter.ModuleRewriter, URLRewriter" name="ModuleRewriter"/> </modules> </system.webServer>

<system.web> <compilation debug="true"/> </system.web>

</configuration></pre>

 本文由用戶 enpc 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!