Uso de NanoHTTPD en Android


Estoy tratando de usar NanoHTTP para servir un archivo HTML. Sin embargo, NanoHTTP es relativamente sin documentar, y soy nuevo en Android. Mi pregunta es, dónde almaceno el archivo html, y cómo específicamente puedo servirlo usando NanoHTTP.

Author: Stephan, 2013-01-14

5 answers

Una respuesta tardía, pero puede ser útil para otros.

Aquí hay un simple servidor web de hola, no exactamente lo que pide, pero puede continuar desde aquí. El siguiente programa supone que tienes un directorio www en la raíz de la tarjeta SD y un archivo index.html dentro.

Actividad Principal Httpd.java:

package com.inforscience.web;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import java.io.*;
import java.util.*;


public class Httpd extends Activity
{
    private WebServer server;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        server = new WebServer();
        try {
            server.start();
        } catch(IOException ioe) {
            Log.w("Httpd", "The server could not start.");
        }
        Log.w("Httpd", "Web server initialized.");
    }


    // DON'T FORGET to stop the server
    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if (server != null)
            server.stop();
    }

    private class WebServer extends NanoHTTPD {

        public WebServer()
        {
            super(8080);
        }

        @Override
        public Response serve(String uri, Method method, 
                              Map<String, String> header,
                              Map<String, String> parameters,
                              Map<String, String> files) {
            String answer = "";
            try {
                // Open file from SD Card
                File root = Environment.getExternalStorageDirectory();
                FileReader index = new FileReader(root.getAbsolutePath() +
                        "/www/index.html");
                BufferedReader reader = new BufferedReader(index);
                String line = "";
                while ((line = reader.readLine()) != null) {
                    answer += line;
                }
                reader.close();

            } catch(IOException ioe) {
                Log.w("Httpd", ioe.toString());
            }


            return new NanoHTTPD.Response(answer);
        }
    }

}

Obviamente la clase NanoHTTPD debe estar en el mismo paquete.

Necesita conceder permiso de internet en AndroidManifest.xml.

<uses-permission android:name="android.permission.INTERNET" />

Y leer almacenamiento externo permiso.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

EDITAR: Para acceder al servidor abra su navegador web con la IP de su dispositivo, por ejemplo, 192.168.1.20:8080.

NOTAS:

 37
Author: rendon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-05 13:33:46

El código fuente bastante bueno se puede encontrar aquí: https://github.com/Teaonly/android-eye

Carpeta de Chceck assets donde se almacenan los archivos html y JavaScript https://github.com/Teaonly/android-eye/tree/master/assets

TeaServer-implementación del servidor https://github.com/Teaonly/android-eye/blob/master/src/teaonly/droideye/TeaServer.java

MainActivity-server inicialización https://github.com/Teaonly/android-eye/blob/master/src/teaonly/droideye/MainActivity.java

 8
Author: MP23,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-20 12:45:28

Actualizado WebServer clase (ver respuesta de rendon) que funciona con la versión actual de NanoHTTPD:

private class WebServer extends NanoHTTPD {

    public WebServer() {
        super(8080);
    }

    @Override
    public Response serve(IHTTPSession session) {
        String answer = "";
        try {
            // Open file from SD Card
            File root = Environment.getExternalStorageDirectory();
            FileReader index = new FileReader(root.getAbsolutePath() +
                    "/www/index.html");
            BufferedReader reader = new BufferedReader(index);
            String line = "";
            while ((line = reader.readLine()) != null) {
                answer += line;
            }
            reader.close();

        } catch(IOException ioe) {
            Log.w("Httpd", ioe.toString());
        }

        return newFixedLengthResponse(answer);
    }

}
 5
Author: Anton Tananaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-18 02:45:43

Prueba esto... Crear 2 paquetes (actividad, util), solo para la organización En actividad crea la clase MainActivity.java en util crear la clase AndroidWebServer.java

package awserverfatepi.com.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import awserverfatepi.com.R;
import awserverfatepi.com.util.AndroidWebServer;

public class MainActivity extends AppCompatActivity {

    private static final int DEFAULT_PORT = 8080;

    private AndroidWebServer androidWebServer;
    private BroadcastReceiver broadcastReceiverNetworkState;
    private static boolean isStarted = false;

    private CoordinatorLayout coordinatorLayout;
    private EditText editTextPort;
    private FloatingActionButton floatingActionButtonOnOff;
    private View textViewMessage;
    private TextView textViewIpAccess;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initGui();
        setIpAccess();
        floatingActionButtonOnOff = (FloatingActionButton) findViewById(R.id.floatingActionButtonOnOff);
        floatingActionButtonOnOff.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isConnectedInWifi()) {
                    if (!isStarted && startAndroidWebServer()) {
                        isStarted = true;
                        textViewMessage.setVisibility(View.VISIBLE);
                        floatingActionButtonOnOff.setBackgroundTintList(ContextCompat.getColorStateList(MainActivity.this,
                                R.color.colorGreen));
                        editTextPort.setEnabled(false);
                    } else if (stopAndroidWebServer()) {
                        isStarted = false;
                        textViewMessage.setVisibility(View.INVISIBLE);
                        floatingActionButtonOnOff.setBackgroundTintList(ContextCompat.getColorStateList(MainActivity.this,
                                R.color.colorRed));
                        editTextPort.setEnabled(true);
                    }
                } else {
                    Snackbar.make(coordinatorLayout, getString(R.string.wifi_message), Snackbar.LENGTH_LONG).show();
                }
            }
        });

        initBroadcastReceiverNetworkStateChanged();
    }

    private void initGui() {
        coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLayout);
        editTextPort = (EditText) findViewById(R.id.editTextPort);
        textViewMessage = findViewById(R.id.textViewMessage);
        textViewIpAccess = (TextView) findViewById(R.id.textViewIpAccess);
    }

    private boolean startAndroidWebServer() {
        if (!isStarted) {
            int port = getPortFromEditText();
            try {
                if (port == 0) {
                    throw new Exception();
                }
                androidWebServer = new AndroidWebServer(port);
                androidWebServer.start();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                Snackbar.make(coordinatorLayout, "A porta " + port + " não está funcionando, por favor altere para outra no intervalo" +
                        " entre 1000 e 9999.", Snackbar.LENGTH_LONG).show();
            }
        }
        return false;
    }

    private boolean stopAndroidWebServer() {
        if (isStarted && androidWebServer != null) {
            androidWebServer.stop();
            return true;
        }
        return false;
    }

    private void setIpAccess() {
        textViewIpAccess.setText(getIpAccess());
    }

    private void initBroadcastReceiverNetworkStateChanged() {
        final IntentFilter filters = new IntentFilter();
        filters.addAction("android.net.wifi.WIFI_STATE_CHANGED");
        filters.addAction("android.net.wifi.STATE_CHANGE");
        broadcastReceiverNetworkState = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                setIpAccess();
            }
        };
        super.registerReceiver(broadcastReceiverNetworkState, filters);
    }

    private String getIpAccess() {
        WifiManager wifiManager = (WifiManager) 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));
        return "http://" + formatedIpAddress + ":";
    }

    private int getPortFromEditText() {
        String valueEditText = editTextPort.getText().toString();
        return (valueEditText.length() > 0) ? Integer.parseInt(valueEditText) : DEFAULT_PORT;
    }

    public boolean isConnectedInWifi() {
        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        NetworkInfo networkInfo = ((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isAvailable() && networkInfo.isConnected()
                && wifiManager.isWifiEnabled() && networkInfo.getTypeName().equals("WIFI")) {
            return true;
        }
        return false;
    }

    public boolean onKeyDown(int keyCode, KeyEvent evt) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (isStarted) {
                new AlertDialog.Builder(this)
                        .setTitle(R.string.warning)
                        .setMessage(R.string.dialog_exit_message)
                        .setPositiveButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                finish();
                            }
                        })
                        .setNegativeButton(getResources().getString(android.R.string.cancel), null)
                        .show();
            } else {
                finish();
            }
            return true;
        }
        return false;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopAndroidWebServer();
        isStarted = false;
        if (broadcastReceiverNetworkState != null) {
            unregisterReceiver(broadcastReceiverNetworkState);
        }
    }

}

En el AndroidWebserver.java

package awserverfatepi.com.util;
import java.util.Map;
import fi.iki.elonen.NanoHTTPD;

public class AndroidWebServer extends NanoHTTPD {

    public AndroidWebServer(int port) {
        super(port);
    }

    public AndroidWebServer(String hostname, int port) {
        super(hostname, port);
    }

    @Override
    public Response serve(IHTTPSession session) {
        String msg = "<html><body><h1>Hello World</h1>\n";
        Map<String, String> parms = session.getParms();
        if (parms.get("username") == null) {
            msg += "<form action='?' method='get'>\n  <p>Seu nome: <input type='text' name='username'></p>\n" + "</form>\n";
        } else {
            msg += "<p>Hello, " + parms.get("username") + "!</p>";
        }
        return newFixedLengthResponse( msg + "</body></html>\n" );
    }
}

No olvides el Manifiesto.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Y último

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:scaleType="centerCrop"
            android:src="@drawable/header" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="@color/colorPrimaryLight"
            android:gravity="center"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textViewIpAccess"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="http://000.000.000.000:"
                    android:textColor="@android:color/white"
                    android:textSize="20sp"
                    android:textStyle="bold" />

                <EditText
                    android:id="@+id/editTextPort"
                    android:layout_width="60dp"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:hint="8080"
                    android:inputType="numberDecimal"
                    android:maxLength="4"
                    android:text="8080"
                    android:textColor="@android:color/white"
                    android:textSize="20sp"
                    android:textStyle="bold" />

            </LinearLayout>

            <TextView
                android:id="@+id/textViewMessage"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="50dp"
                android:layout_marginRight="50dp"
                android:layout_marginTop="50dp"
                android:gravity="center"
                android:text="@string/message"
                android:textColor="@android:color/white"
                android:textSize="18sp"
                android:visibility="invisible" />

        </LinearLayout>

    </LinearLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/floatingActionButtonOnOff"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:elevation="4dp"
        android:src="@drawable/on_btn"
        app:backgroundTint="@color/colorRed" />

</android.support.design.widget.CoordinatorLayout>
 3
Author: Ênio Rodrigues,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-04 02:35:02

Eche un vistazo a cómo sirvo archivos HTML y otros tipos de archivos también. Tengo una clase AndroidWebServer que extiende la clase Nanohttpd. Este es mi método de respuesta -->

    public Response serve(IHTTPSession session) {
    String uri=session.getUri();
    String msg = "<html><body><h1>Hello server</h1>\n";

    File [] arrayfile;

    int i=0;

    try{
        session.parseBody(new HashMap<String, String>());
    }catch (ResponseException | IOException r){
        r.printStackTrace();
    }


    Map<String, String> parms = session.getParms();
    if (parms.get("username") == null) {
        msg += "<form action='?' method='get'>\n  <p>Your name: <input type='text' name='username'></p>\n" + "</form>\n";
    } else {
        msg += "<p>Hello, " + parms.get("username") + "!</p>";
    }
    msg += "<br><br><a href='/Open_rap'>Open Image of Lionel Messi</a><br><br>";
    msg += "<br><br><a href='/files'>Browse Files</a><br><br>";
    msg += "<br><br><a href='/getmethod'>GET METHOD OPERATION</a><br><br>";
    msg += "<br><br><a href='/postmethod'>POST METHOD OPERATION</a><br><br>";
    msg += "<br><br><a href='/jquery'>JQUERY OPERATION</a><br><br>";
    if(uri.equals("/hello")){
            String response="Hello World";
            return  newFixedLengthResponse(response);
    }
    else if(uri.equals("/getmethod")){
        String html="<html><head><h1>Welcome to the Form</h1><head/><body>";

        if(parms.get("name")==null){
            html +="<form action='' method='get'> \n " +
                    "<p>Enter Your Name:</p> <input type='text' name='name'>" +
                    "</form>" +
                    "</body>";
        }
        else{
            html +="<p>Hello Mr. "+ parms.get("name") +"</p>"+
                    "</body> ";
        }

        html +="</html>";
        return newFixedLengthResponse(html);

    }
    else if(uri.equals("/postmethod")){
        String html="<html><head><h1>Welcome to the Form</h1><head/><body>";
        Map<String, String> files = new HashMap<String, String>();
        Method method = session.getMethod();
        String postParameter="";


        html +="<form action='' method='post'> \n " +
                "<p>Enter Your Name:</p> <input type='text' name='name'>" +
                "</form>";

        if (Method.POST.equals(method) || Method.PUT.equals(method)) {
            try {
                session.parseBody(files);
            } catch (IOException ioe) {
                try {
                   // return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d("Exception", e.getMessage());
                }
            } catch (ResponseException re) {
                try {
                   // return newFixedLengthResponse(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d("Exception", re.getMessage());
                }
            }
        }
        html +="</body></html>";
        String postBody = session.getQueryParameterString();
        postParameter = session.getParms().get("name");
        Log.d("Postbody",postBody+"\n"+postParameter);
        if(postParameter!=null){
            String html1="<html><head><h1>"+ postParameter +"</h1><head></html>";
            return newFixedLengthResponse(html1);
        }
        return newFixedLengthResponse(Response.Status.OK,"text/html",html);
    }

    else if(uri.equals("/Open_rap")){

        File root= Environment.getExternalStorageDirectory();
        FileInputStream fis = null;
        File file = new File(root.getAbsolutePath() + "/www/messi.jpg");
        Log.d("Path",root.getAbsolutePath());
        try{
            if(file.exists())
            {
                fis = new FileInputStream(file);

            }
            else
                Log.d("FOF :", "File Not exists:");
        }catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }


        return newFixedLengthResponse(Response.Status.OK,"image/jpeg",fis, file.length() );
    }
  else {
        return newFixedLengthResponse(msg + "</body></html>\n");
    }

}

Como puedes ver, he implementado el método GET y POST. Puedes encontrar cada uno en esta parte del código uri.equals("/getmethod") y uri.equals("/getmethod").
Además, puede ver la parte {>uri.equals("/openrap"), aquí estoy sirviendo un archivo JPG al navegador del cliente y la imagen está presente en el directorio interno en la carpeta /www/.
Hazme un ping si tienes cualquier duda.

 1
Author: Shan Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-31 08:31:26