How to serve a file on sdcard using NanoHTTPD (inside Android)

依然范特西╮ 提交于 2019-11-28 04:34:44

Firstly, you need to make sure you set the mimetype properly when serving media files.

Secondly, you won't get very far reading a MP3 file line by line using FileReader, instead you should provide NanoHTTPD with an InputStream.

Below is a working modified version of your code, which serves a MP3 file. By setting the mimetype to audio/mpeg you let the browser decide what to do with this content. In Chrome, for example, the integrated music player is launched and plays the file.

public class StackOverflowMp3Server extends NanoHTTPD {

    public StackOverflowMp3Server() {
         super(8089);
    }

    @Override
    public Response serve(String uri, Method method,
        Map<String, String> header, Map<String, String> parameters,
        Map<String, String> files) {
    String answer = "";

    FileInputStream fis = null;
    try {
        fis = new FileInputStream(Environment.getExternalStorageDirectory()
                + "/music/musicfile.mp3");
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return new NanoHTTPD.Response(Status.OK, "audio/mpeg", fis);
  }
}

EDIT: a lot of people have been asking how to make the audio file seekable using range requests, I'll demonstrate this below

To make the audio file seekable, range requests are used which enable HTTP clients to retrieve parts of the audio file in chunks. Make sure that you're serving the file with the PARTIAL_CONTENT response status (HTTP 206). An example implementation can be found in the example code of NanoHTTPD: SimpleWebserver.java

In my implementation, instead of returning a NanoHTTPD Response directly in the serve method, I create another method called "servefile" which I use as the response for handling the range requests, as you can see below. This code is a modified implementation of the SimpleWebServer.java I linked above.

  @Override
  public Response serve(String uri, Method method,
        Map<String, String> header, Map<String, String> parameters,
        Map<String, String> files) {

    File f = new File(Environment.getExternalStorageDirectory()
            + "/music/musicfile.mp3");      
    String mimeType =  "audio/mpeg";

    return serveFile(uri, header, f, mimeType);
}
//Announce that the file server accepts partial content requests
private Response createResponse(Response.Status status, String mimeType,
        InputStream message) {
    Response res = new Response(status, mimeType, message);
    res.addHeader("Accept-Ranges", "bytes");
    return res;
}

/**
 * Serves file from homeDir and its' subdirectories (only). Uses only URI,
 * ignores all headers and HTTP parameters.
 */
private Response serveFile(String uri, Map<String, String> header,
        File file, String mime) {
    Response res;
    try {
        // Calculate etag
        String etag = Integer.toHexString((file.getAbsolutePath()
                + file.lastModified() + "" + file.length()).hashCode());

        // Support (simple) skipping:
        long startFrom = 0;
        long endAt = -1;
        String range = header.get("range");
        if (range != null) {
            if (range.startsWith("bytes=")) {
                range = range.substring("bytes=".length());
                int minus = range.indexOf('-');
                try {
                    if (minus > 0) {
                        startFrom = Long.parseLong(range
                                .substring(0, minus));
                        endAt = Long.parseLong(range.substring(minus + 1));
                    }
                } catch (NumberFormatException ignored) {
                }
            }
        }

        // Change return code and add Content-Range header when skipping is
        // requested
        long fileLen = file.length();
        if (range != null && startFrom >= 0) {
            if (startFrom >= fileLen) {
                res = createResponse(Response.Status.RANGE_NOT_SATISFIABLE,
                        NanoHTTPD.MIME_PLAINTEXT, "");
                res.addHeader("Content-Range", "bytes 0-0/" + fileLen);
                res.addHeader("ETag", etag);
            } else {
                if (endAt < 0) {
                    endAt = fileLen - 1;
                }
                long newLen = endAt - startFrom + 1;
                if (newLen < 0) {
                    newLen = 0;
                }

                final long dataLen = newLen;
                FileInputStream fis = new FileInputStream(file) {
                    @Override
                    public int available() throws IOException {
                        return (int) dataLen;
                    }
                };
                fis.skip(startFrom);

                res = createResponse(Response.Status.PARTIAL_CONTENT, mime,
                        fis);
                res.addHeader("Content-Length", "" + dataLen);
                res.addHeader("Content-Range", "bytes " + startFrom + "-"
                        + endAt + "/" + fileLen);
                res.addHeader("ETag", etag);
            }
        } else {
            if (etag.equals(header.get("if-none-match")))
                res = createResponse(Response.Status.NOT_MODIFIED, mime, "");
            else {
                res = createResponse(Response.Status.OK, mime,
                        new FileInputStream(file));
                res.addHeader("Content-Length", "" + fileLen);
                res.addHeader("ETag", etag);
            }
        }
    } catch (IOException ioe) {
        res = createResponse(Response.Status.FORBIDDEN,
                NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed.");
    }

    return res;
}

Don't you also need to add the permission android.permission.READ_EXTERNAL_STORAGE

Because you're reading a file.

this is the code to serve the any type of file

if (uri.equals("/apk")) {
        File root = Environment.getExternalStorageDirectory();
        FileInputStream fis = null;
        File file = new File(root.getAbsolutePath() + "/www/openrap/sunbird.apk");
        String mime = NanoHTTPD.getMimeTypeForFile("sunbird.apk");
        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, mime, fis, file.length());
    }

in Manfiest file add the this permission

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