Search code examples
c#httplistenerhttplistenerrequest

HttpListener how to serve images


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


Solution

  • 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; }
    }