Jak obsługiwać pliki wirtualnych w witrynie C # MVC

głosy
21

Robię catch-all stronę internetową, gdzie użytkownicy mogą przesyłać własne kod HTML na stronie internetowej, a następnie strona pokaże ich stronę, gdy ich rozmowa specyficzna subdomeny.

Kod html z załącznikami uzyskać przesłaniu do podkatalogu wewnątrz stronie internetowej:

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

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

Tak można nazwać te pliki przy użyciu następujących adresów 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

Więc co zrobiłem było zrobić obsługi błędów wewnątrz global.asax, który wykrywa podczas próby zażądać pliku, który nie istnieje, stąd wnioskować stronę internetową:

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

Powyższy kod działa dla „index.html” pliku, ale to nie działa na „/images/logo.png”, ponieważ 404 nie będzie ogień obsługi Application_Error. Po wielu poszukiwania i ciągnięcie moich włosów I okazało się to „funkcja” zaczęło się od .net 4.0 i powyżej. Ale ja nie chcę wracać, chcę wiedzieć, jak prawidłowo rozwiązać ten problem.

Utwórz 08/02/2018 o 19:48
użytkownik
W innych językach...                            


2 odpowiedzi

głosy
3

Czekając, aż błąd aplikacji jest trochę późno w rurociągu. Jednym ze sposobów jest utworzenie niestandardowej obsługi, a przy użyciu niestandardowych trasy w celu wykrycia plików wirtualnych map te wnioski do obsługi. Oznacza to, że trzeba generować linki do plików wirtualnych przy użyciu przewidywalnego wzorca, może dokonywania takich jak ścieżki / SpecialFiles /:

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

Można również mapować to aa działania kontrolera, i niech akcja analizować URL / ciąg kwerendy i powrócić odpowiedź pliku.

Albo podejście pozwala określić trasy z różnymi parametrami, takimi jak wysoko losowej tokena, który jest potrzebny do uzyskania dostępu do pliku typu „udostępniony plik” linki widoczne w innych systemach. Można skonfigurować trasę, aby dopasować na poszczególnych rozszerzeń plików. Opcje są dość zróżnicowane. Podobnie jak w każdej innej trasie, można naciskać różne kawałki toru na zmienne, lub można po prostu otworzyć URL bezpośrednio z żądania, gdy dojdziesz do swojego przewodnika lub działania i przeanalizować go ręcznie.

Odpowiedział 08/02/2018 o 19:56
źródło użytkownik

głosy
0

Dzięki AaronLS, zacząłem poszukiwania jak zrobić niestandardową procedurę obsługi, które złapać wszystkie żądania. Szkoda, że ​​nie było tak łatwe do znalezienia.

Przede wszystkim, trzeba poinformować IIS, które chcesz obsługiwać wszystkie pliki, aktualizując web.config:

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

(Nie znam go httpErrors existingResponse = „passthrough” naprawdę jest potrzebne, może być trochę poprzednie rozwiązanie próbowałem)

Potem musiałem zrobić własny niestandardowy obsługi i ustawić go w 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();
        }
    }
}
Odpowiedział 27/03/2018 o 09:38
źródło użytkownik

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