如何在C#MVC网站中支持虚拟文件



我正在制作一个网站,用户可以将自己的HTML代码上传到网站,然后在呼叫特定子域时,网站将显示他们的网站。

带有附件的HTML代码将其上传到网站内的子目录:

SITE #1
~/sites/test1/index.html
~/sites/test1/images/logo.png
SITE #2
~/sites/test2/index.html
~/sites/test2/images/logo.png

因此,您可以使用以下URL调用这些文件:

SITE #1
http://test1.mydomain.com/index.html
http://test1.mydomain.com/images/logo.png
SITE #2
http://test2.mydomain.com/index.html
http://test2.mydomain.com/images/logo.png

所以我所做的是在global.asax中进行错误处理程序,该操作器检测到您何时尝试请求不存在的文件,因此请求网站:

protected void Application_Error()
{
    // Get the subdomain requested
    var subdomain = Request.Url.Authority.Split(new char[] { '.', ':' }).FirstOrDefault();
    // Get the directory info about the requested subdomain
    DirectoryInfo info = new DirectoryInfo(Server.MapPath("~/" + subdomain));
    // Check if subdomain is not empty and exists
    if (!string.IsNullOrEmpty(subdomain) && info.Exists)
    {
        // Get the requested filename
        var filename = Request.Url.PathAndQuery.Split(new char[] { '?' }).FirstOrDefault();
        // If the root is requested change to index.html
        if (filename == "/") filename = "/index.html";
        // Translate requested filename to server path
        var fullname = Server.MapPath("~/sites/" + subdomain + filename);
        // Respond the file
        ResponseFile(fullname);
    }
    else
    {
        // Subdomain not found so end the request
        Response.End();
    }
}
public void ResponseFile(string fullname)
{
    Response.Clear();
    System.IO.Stream oStream = null;
    try
    {
        // Open the file
        oStream =
            new System.IO.FileStream
                (path: fullname,
                mode: System.IO.FileMode.Open,
                share: System.IO.FileShare.Read,
                access: System.IO.FileAccess.Read);
        // **************************************************
        Response.Buffer = false;
        // Setting the ContentType
        Response.ContentType = MimeMapping.GetMimeMapping(fullname);
        // Get the length of the file 
        long lngFileLength = oStream.Length;
        // Notify user (client) the total file length
        Response.AddHeader("Content-Length", lngFileLength.ToString());
        // **************************************************
        // Total bytes that should be read
        long lngDataToRead = lngFileLength;
        // Read the bytes of file
        while (lngDataToRead > 0)
        {
            // The below code is just for testing! So we commented it!
            //System.Threading.Thread.Sleep(200);
            // Verify that the client is connected or not?
            if (Response.IsClientConnected)
            {
                // 8KB
                int intBufferSize = 8 * 1024;
                // Create buffer for reading [intBufferSize] bytes from file
                byte[] bytBuffers =
                    new System.Byte[intBufferSize];
                // Read the data and put it in the buffer.
                int intTheBytesThatReallyHasBeenReadFromTheStream =
                    oStream.Read(buffer: bytBuffers, offset: 0, count: intBufferSize);
                // Write the data from buffer to the current output stream.
                Response.OutputStream.Write
                    (buffer: bytBuffers, offset: 0,
                    count: intTheBytesThatReallyHasBeenReadFromTheStream);
                // Flush (Send) the data to output
                // (Don't buffer in server's RAM!)
                Response.Flush();
                lngDataToRead =
                    lngDataToRead - intTheBytesThatReallyHasBeenReadFromTheStream;
            }
            else
            {
                // Prevent infinite loop if user disconnected!
                lngDataToRead = -1;
            }
        }
    }
    catch { }
    finally
    {
        if (oStream != null)
        {
            //Close the file.
            oStream.Close();
            oStream.Dispose();
            oStream = null;
        }
        Response.Close();
        Response.End();
    }
}

上面的代码适用于"/index.html"文件,但它对"/images/logo.png"不起作用,因为404不会启动application_error处理程序。经过大量的搜索和拉出头发,我发现此"功能"从.NET 4.0及以上开始。但是我不想回去,我想知道如何正确解决这个问题。

等到管道中的应用程序错误有点晚。一种方法是创建自定义处理程序,并使用自定义路由来检测虚拟文件将这些请求映射到处理程序。这意味着您需要使用可预测的模式生成指向虚拟文件的链接,也许是在/specialfiles/:

之类的路径上。
routes.Add(new Route("SpecialFiles/{*path}", new SomeFileHandler()));

您还可以将其映射到控制器操作中,然后让操作解析URL/QUERY字符串并返回文件响应。

任何一种方法都允许您指定具有各种参数的路由,例如访问类似于其他系统中"共享文件"链接的文件所需的高度随机令牌。您可以配置在特定文件扩展名上匹配的路由。选项非常多样化。就像其他任何路线一样,您可以将路径的不同部分推入变量,也可以在进入处理程序或操作后直接从请求访问URL并手动解析。

多亏了aaronls,我开始搜索如何制作一个可以捕获所有请求的自定义处理程序。太糟糕了,这并不容易找到。

首先,您需要通知IIS您要通过更新Web.config来处理所有文件:

<system.webServer>
    <httpErrors existingResponse="PassThrough" />
    <modules runAllManagedModulesForAllRequests="true">
        <remove name="FormsAuthentication"/>
    </modules>
</system.webServer>

(我不知道它 httperrors asutherResponse =" PassThrough" 确实需要,可能是我尝试过的一些解决方案(

然后,我需要制作自己的自定义处理程序并将其设置在RouteConfig中:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        // So my users can still login
        routes.MapRoute(
            name: "Account",
            url: "Account/{action}/{id}",
            defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional }
        );
        // For the upload controller to work
        routes.MapRoute(
            name: "Upload",
            url: "Upload/{action}/{id}",
            defaults: new { controller = "Upload", action = "Index", id = UrlParameter.Optional }
        );
        // And finally registrating my custom handler
        routes.Add(new Route("{*path}", new CustomRouteHandler()));
        // This was the original routeconfig
        //routes.MapRoute(
        //    name: "Default",
        //    url: "{controller}/{action}/{id}",
        //    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        //);
    }
}
public class CustomRouteHandler : IRouteHandler
{
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new CustomHttpHandler();
    }
}
public class CustomHttpHandler : IHttpHandler
{
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
    public void ProcessRequest(HttpContext context)
    {
        // Get the subdomain requested
        var subdomain = context.Request.Url.Authority.Split(new char[] { '.', ':' }).FirstOrDefault();
        // Get the directory info about the requested subdomain
        DirectoryInfo info = new DirectoryInfo(context.Server.MapPath("~/Websites/" + subdomain));
        // Check if subdomain is not empty and exists
        if (!string.IsNullOrEmpty(subdomain) && info.Exists)
        {
            // Get the requested filename
            var filename = context.Request.Url.PathAndQuery.Split(new char[] { '?' }).FirstOrDefault();
            // If the root is requested change to index.html
            if (filename == "/") filename = "/index.html";
            // Translate requested filename to server path
            var fullname = context.Server.MapPath("~/Websites/" + subdomain + filename);
            // Respond the file
            ResponseFile(context, fullname);
        }
        else
        {
            // Subdomain not found so end the request
            context.Response.End();
        }
    }
    public void ResponseFile(HttpContext context, string fullname)
    {
        // Clear the response buffer
        context.Response.Clear();
        System.IO.Stream oStream = null;
        try
        {
            // Open the file
            oStream =
                new System.IO.FileStream
                    (path: fullname,
                    mode: System.IO.FileMode.Open,
                    share: System.IO.FileShare.Read,
                    access: System.IO.FileAccess.Read);
            // **************************************************
            context.Response.Buffer = false;
            // Setting the ContentType
            context.Response.ContentType = MimeMapping.GetMimeMapping(fullname);
            // Get the length of the file 
            long lngFileLength = oStream.Length;
            // Notify user (client) the total file length
            context.Response.AddHeader("Content-Length", lngFileLength.ToString());
            // **************************************************
            // Total bytes that should be read
            long lngDataToRead = lngFileLength;
            // Read the bytes of file
            while (lngDataToRead > 0)
            {
                // Verify that the client is connected or not?
                if (context.Response.IsClientConnected)
                {
                    // 8KB
                    int intBufferSize = 8 * 1024;
                    // Create buffer for reading [intBufferSize] bytes from file
                    byte[] bytBuffers =
                        new System.Byte[intBufferSize];
                    // Read the data and put it in the buffer.
                    int intTheBytesThatReallyHasBeenReadFromTheStream =
                        oStream.Read(buffer: bytBuffers, offset: 0, count: intBufferSize);
                    // Write the data from buffer to the current output stream.
                    context.Response.OutputStream.Write
                        (buffer: bytBuffers, offset: 0,
                        count: intTheBytesThatReallyHasBeenReadFromTheStream);
                    // Flush (Send) the data to output
                    // (Don't buffer in server's RAM!)
                    context.Response.Flush();
                    lngDataToRead =
                        lngDataToRead - intTheBytesThatReallyHasBeenReadFromTheStream;
                }
                else
                {
                    // Prevent infinite loop if user disconnected!
                    lngDataToRead = -1;
                }
            }
        }
        catch (Exception e)
        {
        }
        finally
        {
            if (oStream != null)
            {
                //Close the file.
                oStream.Close();
                oStream.Dispose();
                oStream = null;
            }
            context.Response.Close();
            context.Response.End();
        }
    }
}

最新更新