概要

前回のプログラムではHTMLしか返さないのでWebページに画像が含まれていても表示できませんでした。そのため今回のプログラムでは要求されたファイルの拡張子で画像かどうかを判断し、HTMLと画像で処理を分岐させています。事前に対応する画像の拡張子はSetに登録しておきます。また前回はHTMLだけを出力することを考えていたのでPrintWriterを使っていましたが今回から画像も出力することを考慮しPrintStreamを使っています。

ソースコード

import java.net.Socket;
import java.net.ServerSocket;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import java.io.PrintStream;
import java.io.BufferedOutputStream;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.charset.StandardCharsets;

import java.util.Set;
import java.util.HashSet;

import java.io.IOException;

public class HttpServerSample{
    //画像ファイルの拡張子を登録するSet
    private static final Set<String> IMAGE_SET;
    
    static{
        IMAGE_SET = new HashSet<>();
        
        //画像ファイルとして扱う拡張子を登録していく
        IMAGE_SET.add("jpg");
        IMAGE_SET.add("jpeg");
        IMAGE_SET.add("png");
    }
    
    public static void main(String... args){
        //ポート12435でServerSocket作成
        try(var server = new ServerSocket(12435)){
            HttpServerSample.accept(server);
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    private static void accept(ServerSocket server){
        while(true){
            //クライアントからの接続を待機する
            try(var socket = server.accept()){
                //クライアントから接続されたときの処理
                HttpServerSample.connectHttp(socket);
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }

    private static void connectHttp(Socket socket) throws IOException{
        try(
            var in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
            var out = new PrintStream(new BufferedOutputStream(socket.getOutputStream()), true);
        ){
            String line = in.readLine();

            if(line != null){
                var tokens = line.split(" ");
                var fileName = tokens.length == 3 ? tokens[1] : "index.html";

                if(fileName.equals("/")){
                    fileName = "index.html";
                }

                System.out.printf("Request FileName: %s\n", fileName);

                while((line = in.readLine()) != null){
                    System.out.println(line);

                    if(line.isEmpty()){
                        break;
                    }
                }

                HttpServerSample.printFile(out, fileName);
            }
        }
    }

    private static void printFile(PrintStream out, String fileName) throws IOException{
        var path = Paths.get("www", fileName);

        if(Files.exists(path)){
            String extension = HttpServerSample.getFileExtension(path).toLowerCase();
            
            if(IMAGE_SET.contains(extension)){
                //拡張子から画像として判断し画像ファイルを読み込んでそのまま返す
                var image = Files.readAllBytes(path);
                
                out.println("HTTP/1.1 200 OK");
                out.println("Content-Type: image/" + extension);
                out.println("Content-Length: " + image.length);
                out.println("");
                
                out.write(image);
            }else{
                //画像でなければHTMLとして返す
                out.println("HTTP/1.1 200 OK");
                out.println("Content-Type: text/html; charset=UTF-8");
                out.println();
                Files.lines(path).forEach(out::println);
            }
        }else{
            //ファイルが見つからなければ404を返す
            out.println("HTTP/1.1 404 Not Found");
            out.println("Content-Type: text/html; charset=UTF-8");
            out.println();
            out.println("<html><title><head>404 Not Found</head></title><body><h1>404 Not Found</h1></body></html>");
            
            System.err.printf("%s is Not Found", path.toAbsolutePath().toString());
        }
    }
    
    private static String getFileExtension(Path path) {
        String fileName = path.getFileName().toString();
        int index = fileName.lastIndexOf('.'); //ファイル名の最後のドットの位置を調べる

        //ファイル名にドットが無い場合は拡張子なしとして空文字列を返す
        if(index == -1){
            return ""; // 拡張子なし
        }

        //ファイル名にドットがあればそれ以降を返す
        return fileName.substring(index + 1);
    }
}

実行結果のサンプル

wwwディレクトリにIMGタグを含むHTMLと画像を入れることで以下のように表示させることができます。次回は音声ファイルや動画ファイルにも対応していきます。