Using nanohttpd for server on localhost, how to serve the static HTML code in the whole directory?
Asked Answered
S

2

9

I can't make nanohttpd work. It seems not be able to find the www directory in app's root.

My code is at https://github.com/tlkahn/neonx

My code at MainActivity.java:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BottomNavigationView navView = findViewById(R.id.nav_view);
        mWebView = findViewById(R.id.webkit);
        navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        webSettings.setDomStorageEnabled(true);
        mWebView.getSettings().setLoadsImagesAutomatically(true);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                return false;
            }
        });
        if (!haveNetworkConnection()) {
            new AlertDialog.Builder(this)
                .setTitle("You are not connected to internet.")
                .setMessage("Are you sure you want to exit?")
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        finishAffinity();
                        System.exit(0);
                    }
                }).setNegativeButton("No", null).show();
        }
        startLocalServer(3000, "www", true, true );
    }

  public void startLocalServer(int port, String root, Boolean localhost, Boolean keepAlive) {
      try {
          File www_root = new File(root);
          server = new WebServer("localhost", port, www_root.getAbsoluteFile());
          server.start();
          printIp();
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

When I tried to visit localhost:3000, I got the error: given path is not a directory. The error seems to come from this line: https://git.io/fjS3f

I guess the way I initialize the rootDir is wrong (this line: https://git.io/fjS3v). But how can I make this work? I mean to serve the whole directory, which means all CSS/JS/hypyerlinks should work, once nanohttpd starts serving...

Schlieren answered 29/7, 2019 at 14:33 Comment(0)
C
6

Output: enter image description here

LogCat:

2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: -------Assets List-----
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: asset-manifest.json
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: favicon.ico
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: index.html
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: manifest.json
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: precache-manifest.81af63d07b6dd6ae8e331187c522b020.js
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: service-worker.js
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: static
2019-08-05 15:21:53.842 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www
2019-08-05 15:21:53.865 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static
2019-08-05 15:21:53.867 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static/css targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static/css
2019-08-05 15:21:53.922 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static/js targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static/js
2019-08-05 15:21:54.352 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static/media targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static/media
2019-08-05 15:21:54.526 10650-10650/com.neonxorg.neonx E/MainActivity: -------Root File List-----
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/precache-manifest.81af63d07b6dd6ae8e331187c522b020.js
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/service-worker.js
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/favicon.ico
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/manifest.json
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/asset-manifest.json
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/index.html
2019-08-05 15:21:54.704 10650-10650/com.neonxorg.neonx E/MainActivity: Connected : Please access! http://192.168.1.2:3000 From a web browser

Code:

public final String TAG = getClass().getSimpleName();
public void startLocalServer(int port, String root, Boolean localhost, Boolean keepAlive) {
        try {
            String[] filePathList = (getAssets().list("www"));
            Log.e(TAG,"-------Assets List-----");
            for (String s : filePathList) {
                Log.e(TAG, s);
            }
            File externalCache = getExternalCacheDir();
            if (externalCache != null) {
                String path = externalCache.getAbsolutePath() + "/" + root;
                copyFolderFromAssets(getApplicationContext(), "www", path);
                File www_root = new File(path);
                Log.e(TAG,"-------Root File List-----");
                for (File f : www_root.listFiles()) {
                    Log.e("File ", f.getAbsolutePath());
                }
                server = new WebServer("localhost", port, www_root.getCanonicalFile());
                server.start();
                printIp();
            }

        } catch (IOException e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

    public void copyFolderFromAssets(Context context, String rootDirFullPath, String targetDirFullPath) {
        Log.e(TAG,"copyFolderFromAssets " + "rootDirFullPath-" + rootDirFullPath + " targetDirFullPath-" + targetDirFullPath);
        File file = new File(targetDirFullPath);
        if (!file.exists()) {
            new File(targetDirFullPath).mkdirs();
        }
        try {
            String[] listFiles = context.getAssets().list(rootDirFullPath);// 遍历该目录下的文件和文件夹
            for (String string : listFiles) {// 看起子目录是文件还是文件夹,这里只好用.做区分了
                if (isFileByName(string)) {// 文件
                    copyFileFromAssets(context, rootDirFullPath + "/" + string, targetDirFullPath + "/" + string);
                } else {// 文件夹
                    String childRootDirFullPath = rootDirFullPath + "/" + string;
                    String childTargetDirFullPath = targetDirFullPath + "/" + string;
                    new File(childTargetDirFullPath).mkdirs();
                    copyFolderFromAssets(context, childRootDirFullPath, childTargetDirFullPath);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }


    public void copyFileFromAssets(Context context, String assetsFilePath, String targetFileFullPath) {
        InputStream assestsFileInputStream;
        try {
            assestsFileInputStream = context.getAssets().open(assetsFilePath);
            FileOutputStream fOS = new FileOutputStream(new File(targetFileFullPath));
            int length = -1;
            byte[] buf = new byte[1024];
            while ((length = assestsFileInputStream.read(buf)) != -1) {
                fOS.write(buf, 0, length);
            }
            fOS.flush();
        } catch (IOException e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

    private boolean isFileByName(String str) {
        return str.contains(".");
    }

    private void printIp() {
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
        int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
        final String formatedIpAddress = String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff),
                (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
        Log.e(TAG,"Connected : " + "Please access! http://" + formatedIpAddress + ":" + server.getListeningPort() + " From a web browser");
    }

given path is not a directory.

When nanphttpd is not able to locate the data then it gives this error.

Why You are not getting the actual error

In the catch block of copyFolderFromAssets and copyFileFromAssets you are using e.printStackTrace() which may not be showing on your LogCat due to Show only selected application filter

enter image description here

In order to print the errors, you need to use the following:

Log.e(TAG, Log.getStackTraceString(e));

I replaced all of your System.out and e.printStackTrace with Log.e statements. Most likely the app wasn't able to copy contents from www directory to target directory. I changed target directory to cache directory and it worked on my device. (see below):

File externalCache = getExternalCacheDir();
if (externalCache != null) {
    String path = externalCache.getAbsolutePath() + "/" + root;
    File www_root = new File(path);
    copyFolderFromAssets(getApplicationContext(), "www", path);
    Log.e(TAG,"-------Root File List-----");
    for (File f : www_root.listFiles()) {
        Log.e("File ", f.getAbsolutePath());
    }
    server = new WebServer("localhost", port, www_root.getCanonicalFile());
    server.start();
    printIp();
}

Sidenote:

  • There is no need of using static keyword in those function until or unless you want to copy them in a utility class
Chlortetracycline answered 5/8, 2019 at 10:13 Comment(0)
A
8

The problem is that you cannot access files in the assets folder as if they were local files. You have to extend NanoHTTPD and override serve(IHTTPSession) in order to serve assets. Here is an example in Kotlin. Let me know if you cannot read it and I will port it to Java.

class FileServer(private val context: Context, port: Int) : NanoHTTPD(port) {
override fun serve(session: IHTTPSession): Response {
    val uri = session.uri.removePrefix("/").ifEmpty { "index.html" }
    println("Loading $uri")
    try {
        val mime = when (uri.substringAfterLast(".")) {
            "ico" -> "image/x-icon"
            "css" -> "text/css"
            "htm" -> "text/html"
            "html" -> "text/html"
            else -> "application/javascript"
        }

        return NanoHTTPD.newChunkedResponse(
            Response.Status.OK,
            mime,
            context.assets.open("www/$uri") // prefix with www because your files are not in the root folder in assets
        )
    } catch (e: Exception) {
        val message = "Failed to load asset $uri because $e"
        println(message)
        e.printStackTrace()
        return NanoHTTPD.newFixedLengthResponse(message)
    }
}
Anthiathia answered 3/8, 2019 at 12:53 Comment(1)
thanks, I couldn't find an other exemple to serve static files with nanoHttpd.Dortch
C
6

Output: enter image description here

LogCat:

2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: -------Assets List-----
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: asset-manifest.json
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: favicon.ico
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: index.html
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: manifest.json
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: precache-manifest.81af63d07b6dd6ae8e331187c522b020.js
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: service-worker.js
2019-08-05 15:21:53.838 10650-10650/com.neonxorg.neonx E/MainActivity: static
2019-08-05 15:21:53.842 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www
2019-08-05 15:21:53.865 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static
2019-08-05 15:21:53.867 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static/css targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static/css
2019-08-05 15:21:53.922 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static/js targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static/js
2019-08-05 15:21:54.352 10650-10650/com.neonxorg.neonx E/MainActivity: copyFolderFromAssets rootDirFullPath-www/static/media targetDirFullPath-/storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static/media
2019-08-05 15:21:54.526 10650-10650/com.neonxorg.neonx E/MainActivity: -------Root File List-----
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/precache-manifest.81af63d07b6dd6ae8e331187c522b020.js
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/service-worker.js
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/static
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/favicon.ico
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/manifest.json
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/asset-manifest.json
2019-08-05 15:21:54.528 10650-10650/com.neonxorg.neonx E/File: /storage/emulated/0/Android/data/com.neonxorg.neonx/cache/www/index.html
2019-08-05 15:21:54.704 10650-10650/com.neonxorg.neonx E/MainActivity: Connected : Please access! http://192.168.1.2:3000 From a web browser

Code:

public final String TAG = getClass().getSimpleName();
public void startLocalServer(int port, String root, Boolean localhost, Boolean keepAlive) {
        try {
            String[] filePathList = (getAssets().list("www"));
            Log.e(TAG,"-------Assets List-----");
            for (String s : filePathList) {
                Log.e(TAG, s);
            }
            File externalCache = getExternalCacheDir();
            if (externalCache != null) {
                String path = externalCache.getAbsolutePath() + "/" + root;
                copyFolderFromAssets(getApplicationContext(), "www", path);
                File www_root = new File(path);
                Log.e(TAG,"-------Root File List-----");
                for (File f : www_root.listFiles()) {
                    Log.e("File ", f.getAbsolutePath());
                }
                server = new WebServer("localhost", port, www_root.getCanonicalFile());
                server.start();
                printIp();
            }

        } catch (IOException e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

    public void copyFolderFromAssets(Context context, String rootDirFullPath, String targetDirFullPath) {
        Log.e(TAG,"copyFolderFromAssets " + "rootDirFullPath-" + rootDirFullPath + " targetDirFullPath-" + targetDirFullPath);
        File file = new File(targetDirFullPath);
        if (!file.exists()) {
            new File(targetDirFullPath).mkdirs();
        }
        try {
            String[] listFiles = context.getAssets().list(rootDirFullPath);// 遍历该目录下的文件和文件夹
            for (String string : listFiles) {// 看起子目录是文件还是文件夹,这里只好用.做区分了
                if (isFileByName(string)) {// 文件
                    copyFileFromAssets(context, rootDirFullPath + "/" + string, targetDirFullPath + "/" + string);
                } else {// 文件夹
                    String childRootDirFullPath = rootDirFullPath + "/" + string;
                    String childTargetDirFullPath = targetDirFullPath + "/" + string;
                    new File(childTargetDirFullPath).mkdirs();
                    copyFolderFromAssets(context, childRootDirFullPath, childTargetDirFullPath);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }


    public void copyFileFromAssets(Context context, String assetsFilePath, String targetFileFullPath) {
        InputStream assestsFileInputStream;
        try {
            assestsFileInputStream = context.getAssets().open(assetsFilePath);
            FileOutputStream fOS = new FileOutputStream(new File(targetFileFullPath));
            int length = -1;
            byte[] buf = new byte[1024];
            while ((length = assestsFileInputStream.read(buf)) != -1) {
                fOS.write(buf, 0, length);
            }
            fOS.flush();
        } catch (IOException e) {
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

    private boolean isFileByName(String str) {
        return str.contains(".");
    }

    private void printIp() {
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
        int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
        final String formatedIpAddress = String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff),
                (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
        Log.e(TAG,"Connected : " + "Please access! http://" + formatedIpAddress + ":" + server.getListeningPort() + " From a web browser");
    }

given path is not a directory.

When nanphttpd is not able to locate the data then it gives this error.

Why You are not getting the actual error

In the catch block of copyFolderFromAssets and copyFileFromAssets you are using e.printStackTrace() which may not be showing on your LogCat due to Show only selected application filter

enter image description here

In order to print the errors, you need to use the following:

Log.e(TAG, Log.getStackTraceString(e));

I replaced all of your System.out and e.printStackTrace with Log.e statements. Most likely the app wasn't able to copy contents from www directory to target directory. I changed target directory to cache directory and it worked on my device. (see below):

File externalCache = getExternalCacheDir();
if (externalCache != null) {
    String path = externalCache.getAbsolutePath() + "/" + root;
    File www_root = new File(path);
    copyFolderFromAssets(getApplicationContext(), "www", path);
    Log.e(TAG,"-------Root File List-----");
    for (File f : www_root.listFiles()) {
        Log.e("File ", f.getAbsolutePath());
    }
    server = new WebServer("localhost", port, www_root.getCanonicalFile());
    server.start();
    printIp();
}

Sidenote:

  • There is no need of using static keyword in those function until or unless you want to copy them in a utility class
Chlortetracycline answered 5/8, 2019 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.