HttpListener how to serve images

倾然丶 夕夏残阳落幕 提交于 2020-01-30 06:55:07

问题


I'm making a simple webserver to serve html, css, js & images (done in c#). I am using HttpListener and I can get the html, javascript and css files to work properly. I am just having trouble with the images. This is what I'm using currently:

        if (request.RawUrl.ToLower().Contains(".png") || request.RawUrl.Contains(".ico") || request.RawUrl.ToLower().Contains(".jpg") || request.RawUrl.ToLower().Contains(".jpeg"))
        {
                string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                string[] img = request.RawUrl.Split('/');
                string path = dir + @"\public\imgs\" + img[img.Length - 1];

                FileInfo fileInfo = new FileInfo(path);
                long numBytes = fileInfo.Length;

                FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
                BinaryReader binaryReader = new BinaryReader(fileStream);
                byte[] output = binaryReader.ReadBytes((int)numBytes);
                binaryReader.Close();
                fileStream.Close();

                var temp = System.Text.Encoding.UTF8.GetString(output);
                return temp;
            }

I am converting the image into a string to return them (it's the way my boss suggested). This is the method where I am handling these requests.

private static string SendResponse(HttpListenerRequest request)

This is my WebServer classes Run() method. The call to SetContentType just goes through the request.RawUrl and determines the content type.

public void Run()
    {
        ThreadPool.QueueUserWorkItem((o) =>
        {
            Console.WriteLine("StackLight Web Server is running...");

            try
            {
                while (_listener.IsListening)
                {
                    ThreadPool.QueueUserWorkItem((c) =>
                    {
                        var ctx = c as HttpListenerContext;

                        try
                        {
                            // store html content in a byte array
                            string responderString = _responderMethod(ctx.Request);

                            // set the content type
                            ctx.Response.Headers[HttpResponseHeader.ContentType] = SetContentType(ctx.Request.RawUrl);

                            byte[] buffer = buffer = Encoding.UTF8.GetBytes(responderString);


                            // this writes the html out from the byte array
                            ctx.Response.ContentLength64 = buffer.Length;
                            using(Stream stream = ctx.Response.OutputStream)
                            {
                                stream.Write(buffer, 0, buffer.Length);
                            }
                        }
                        catch (Exception ex)
                        {
                            ConfigLogger.Instance.LogCritical(LogCategory, ex);
                        }
                    }, _listener.GetContext());
                }
            }
            catch (Exception ex)
            {
                ConfigLogger.Instance.LogCritical(LogCategory, ex); 
            }
        });
    }

My html page needs to display an image to the screen, it displays a broken image so far. I know the images directory is correct, I tested that.

This is where I got my code for the webserver: here

I was thinking that maybe I have to change the SendResponse method to not return a string


回答1:


I figured it out. I created a class to hold the data, content type and the request.RawUrl. Then, where I was passing a string, I changed it to pass the object I created.

So, for my WebServer class, my Run method looks like this:

public void Run()
    {
        ThreadPool.QueueUserWorkItem((o) =>
        {
            Console.WriteLine("StackLight Web Server is running...");

            try
            {
                while (_listener.IsListening)
                {
                    ThreadPool.QueueUserWorkItem((c) =>
                    {
                        var ctx = c as HttpListenerContext;

                        try
                        {
                            // set the content type
                            ctx.Response.Headers[HttpResponseHeader.ContentType] = SetContentType(ctx.Request.RawUrl);
                            WebServerRequestData data = new WebServerRequestData();

                            // store html content in a byte array
                            data = _responderMethod(ctx.Request);

                            string res = "";
                            if(data.ContentType.Contains("text"))
                            {
                                char[] chars = new char[data.Content.Length/sizeof(char)];
                                System.Buffer.BlockCopy(data.Content, 0, chars, 0, data.Content.Length);
                                res = new string(chars);
                                data.Content = Encoding.UTF8.GetBytes(res);
                            }

                            // this writes the html out from the byte array
                            ctx.Response.ContentLength64 = data.Content.Length;
                            ctx.Response.OutputStream.Write(data.Content, 0, data.Content.Length);
                        }
                        catch (Exception ex)
                        {
                            ConfigLogger.Instance.LogCritical(LogCategory, ex);
                        }
                        finally
                        {
                            ctx.Response.OutputStream.Close();
                        }
                    }, _listener.GetContext());
                }
            }
            catch (Exception ex)
            {
                ConfigLogger.Instance.LogCritical(LogCategory, ex); 
            }
        });
    }

And my SendResponse method looks like this:

private static WebServerRequestData SendResponse(HttpListenerRequest request)
    {
        string dir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        string[] fileUrl = request.RawUrl.Split('/');

        // routes
        if (request.RawUrl.Contains("/"))
        {
            // this is the main page ('/'), all other routes can be accessed from here (including css, js, & images)
            if (request.RawUrl.ToLower().Contains(".png") || request.RawUrl.ToLower().Contains(".ico") || request.RawUrl.ToLower().Contains(".jpg") || request.RawUrl.ToLower().Contains(".jpeg"))
            {
                try
                {
                    string path = dir + Properties.Settings.Default.ImagesPath + fileUrl[fileUrl.Length - 1];

                    FileInfo fileInfo = new FileInfo(path);
                    path = dir + @"\public\imgs\" + fileInfo.Name;

                    byte[] output = File.ReadAllBytes(path);

                    _data = new WebServerRequestData() {Content = output, ContentType = "image/png", RawUrl = request.RawUrl};
                    //var temp = System.Text.Encoding.UTF8.GetString(output);

                    //return Convert.ToBase64String(output);
                    return _data;
                }
                catch(Exception ex)
                {
                    ConfigLogger.Instance.LogError(LogCategory, "File could not be read.");
                    ConfigLogger.Instance.LogCritical(LogCategory, ex);
                    _errorString = string.Format("<html><head><title>Test</title></head><body>There was an error processing your request:<br />{0}</body></html>", ex.Message);
                    _byteData = new byte[_errorString.Length * sizeof(char)];
                    System.Buffer.BlockCopy(_errorString.ToCharArray(), 0, _byteData, 0, _byteData.Length);

                    _data = new WebServerRequestData() { Content = _byteData, ContentType = "text/html", RawUrl = request.RawUrl };
                    return _data;
                }
            }

I'm still cleaning up the code a bit but it now serves the images!

Oh... And here is the object I'm using:

public class WebServerRequestData
{
    public string RawUrl { get; set; }
    public string ContentType { get; set; }
    public byte[] Content { get; set; }
    public string RawData { get; set; }
}



回答2:


Some really bad stuff here:

  1. Empty catch. You'll never find out about many bugs.
  2. Stuffing binary data into a string. Why? There's no encoding that is able to roundtrip binary data.
  3. You're not disposing of ctx. I don't see why you need a manual finally block. Use using.
  4. Untrusted callers can inject arbitrary paths into path. I could request your web.config file by navigating to /img/..\..\web.config (something like that).

Consider factoring out some common expressions into variables. You've got a Copy&Paste error with ToLower. Don't do dirty stuff and you'll have less bugs.



来源:https://stackoverflow.com/questions/26513743/httplistener-how-to-serve-images

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!