Como suporte a arquivos virtuais em um site C # MVC

votos
21

Eu estou fazendo um catch-all site onde os usuários podem fazer upload de seu próprio código html para o site, em seguida, o site irá mostrar o seu site quando a sua chamada o subdomínio específico.

O código html com anexos se é carregado para um subdiretório dentro do site:

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

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

Então você pode chamar esses arquivos usando as seguintes urls:

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

Então o que eu fiz foi fazer manipulador de erro dentro do global.asax que detecta quando você tenta solicitar um arquivo que não existe, portanto, solicitar o site:

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();
    }
}

O código acima funciona para o arquivo index.html, mas ele não funciona para o /images/logo.png porque o 404 não dispara o manipulador Application_Error. Depois de muita pesquisa e puxando meu cabelo para fora eu descobri este recurso começou a partir de .NET 4.0 e acima. Mas eu não quero voltar, eu quero saber como resolver corretamente este.

Publicado 08/02/2018 em 19:48
usuário
Em outras línguas...                            


2 respostas

votos
3

Esperar até erro de aplicação é um pouco tarde no pipeline. Uma maneira é criar um manipulador personalizado, e usando uma rota personalizada para detectar arquivos virtuais mapear essas solicitações para o manipulador. Isso significa que você precisa para gerar links para arquivos virtuais usando um padrão previsível, talvez fazendo um caminho como / SpecialFiles /:

routes.Add(new Route("SpecialFiles/{*path}", new SomeFileHandler()));

Você também pode mapear isso para aa ação do controlador, e deixe a ação analisar a cadeia de URL / consulta e retornar uma resposta arquivo.

Qualquer abordagem permite que você especifique um percurso com vários parâmetros, tais como um símbolo altamente aleatória que é necessário para acessar o arquivo semelhante a "arquivos compartilhados" links visto em outros sistemas. Você pode configurar a rota para corresponder em determinadas extensões de arquivo. As opções são bastante variadas. Assim como qualquer outra rota, você pode empurrar diferentes peças do caminho em variáveis, ou você pode simplesmente acessar a URL diretamente do pedido, uma vez que você entrar em seu manipulador ou ação e analisá-lo manualmente.

Respondeu 08/02/2018 em 19:56
fonte usuário

votos
0

Graças a AaronLS, comecei a procurar como fazer um manipulador personalizado que iria pegar todos os pedidos. Pena que não era tão fácil de encontrar.

Primeiro de tudo, você precisa informar IIS que você deseja manipular todos os arquivos através da actualização do web.config:

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

(Eu não sei que o "PassThrough" httpErrors existingResponse = realmente é necessário, poderia ter sido alguma solução anterior eu tentei)

Então eu precisava para fazer o meu próprio manipulador personalizado e configurá-lo na 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();
        }
    }
}
Respondeu 27/03/2018 em 09:38
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more