如何使用NanoHTTPD担任SD卡的文件(里面安卓) [英] How to serve a file on sdcard using NanoHTTPD (inside Android)

查看:1689
本文介绍了如何使用NanoHTTPD担任SD卡的文件(里面安卓)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用NanoHTTPD写了一个小的Andr​​oid服务器。它可以作为一个HTML文件以及(网页位于SD卡/网络/ index.html的)。任何人都可以请帮我找出我如何可以为音频或视频文件,而不是使用NanoHTTPD一个html页面?请原谅我,如果这个问题似乎是愚蠢的,因为我是新来的HTTP!这是我的服务器端code(我把它换成网页路径,音频文件):

 包com.example.zserver;

进口java.io.BufferedReader中;
进口的java.io.File;
进口java.io.FileReader;
进口java.io.IOException异常;
进口的java.util.Map;

进口android.app.Activity;
进口android.os.Bundle;
进口android.os.Environment;
进口android.util.Log;

公共类MainActivity延伸活动{

    私人WebServer的服务器;
    @覆盖
    保护无效的onCreate(包savedInstanceState){
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.activity_main);

        服务器=新的Web服务器();
        尝试 {
            server.start();
        }赶上(IOException异常IOE){
            Log.w(包含httpd,服务器无法启动。);
        }
        Log.w(包含httpd,Web服务器初始化。);

    }
    @覆盖
    公共无效的onDestroy()
    {
        super.onDestroy();
        如果(服务器!= NULL)
            server.stop();
    }

    私有类Web服务器扩展NanoHTTPD {

        公开的Web服务器()
        {
            超(8080);
        }

        @覆盖
        公众的反应发球(字符串URI,方法的方法,
                              地图<字符串,字符串>头,
                              地图<字符串,字符串>参数,
                              地图<字符串,字符串>文件){
            字符串的答案=;
            尝试 {
                //打开文件的SD卡
                文件根= Environment.getExternalStorageDirectory();
                的FileReader指数=新的FileReader(root.getAbsolutePath()+
                        /www/music.mp3);
                的BufferedReader读卡器=新的BufferedReader(指数);
                串线=;
                而((行= reader.readLine())!= NULL){
                    答案+ =行;
                }

            }赶上(IOException异常IOE){
                Log.w(包含httpd,ioe.toString());
            }


            返回新NanoHTTPD.Response(答案);
        }
    }

}
 

我在我的code包括配套使用,权限:

 <使用-权限的Andr​​oid:名称=android.permission.INTERNET对/>
<使用-权限的Andr​​oid:名称=android.permission.WRITE_EXTERNAL_STORAG​​E/>
 

任何帮助将是AP preciated。在此先感谢!

编辑,添加权限:

 <使用-权限的Andr​​oid:名称=android.permission.READ_EXTERNAL_STORAG​​E/>
 

EDIT1:读书规范的方法和写入缓存:

  INT读取;
        INT N = 1;
        而((读= dis.read(mybytearray))!= -1){
            dos.write(mybytearray,0,读);}
 

解决方案

首先,你需要确保你正确地提供媒体文件时,设置媒体类型。

其次,你不会得到很远,使用读取MP3文件一行一行的FileReader ,而不是你应该提供NanoHTTPD与的InputStream

下面是你的code,它提供MP3文件的一个工作修改后的版本。采用mimeType设置为音频/ MPEG 你让浏览器决定做什么与此内容。在浏览器,例如,集成的音乐播放器被启动,并播放该文件。

公共类StackOverflowMp3Server扩展NanoHTTPD {     公共StackOverflowMp3Server(){          超(8089);     }     @覆盖     公众的反应发球(字符串URI,方法的方法,         地图<字符串,字符串>头,地图<字符串,字符串>参数,         地图<字符串,字符串>文件){     字符串的答案=;     的FileInputStream FIS = NULL;     尝试 {         FIS =新的FileInputStream(Environment.getExternalStorageDirectory()                 +/music/musicfile.mp3);     }赶上(FileNotFoundException异常E){         // TODO自动生成的catch块         e.printStackTrace();     }     返回新NanoHTTPD.Response(Status.OK,音频/ MPEG,FIS);   } }

编辑:有很多人一直在问如何使音频文件,可查找使用范围请求,我会证明这一点下面

若要使音频文件可查找,范围请求用于哪些启用HTTP客户以检索该音频文件的部分块中。请确保你提供的PARTIAL_CONTENT响应状态(HTTP 206)的文件。一个例子可以实现在NanoHTTPD的例子code被发现:<一href="https://github.com/NanoHttpd/nanohttpd/blob/master/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java">SimpleWebserver.java

在我的实现,而不是直接在服务方法返回一个NanoHTTPD响应,创建另一个名为servefile这是我作为处理范围的请求,你可以看到下面的响应使用方法。这code是SimpleWebServer.java我上面链接的修改实施。

@覆盖   公众的反应发球(字符串URI,方法的方法,         地图&LT;字符串,字符串&GT;头,地图&LT;字符串,字符串&GT;参数,         地图&LT;字符串,字符串&GT;文件){     文件F =新的文件(Environment.getExternalStorageDirectory()             +/music/musicfile.mp3);     字符串MIMETYPE =音频/ MPEG;     返回serveFile(URI,头,男,MIMETYPE); } //宣布,文件服务器接受部分内容的请求 私人响应createResponse(Response.Status状态,串MIMETYPE,         InputStream的消息){     响应RES =新的响应(状态,MIMETYPE,消息);     res.addHeader(接受范围,字节);     返回水库; } / **  *从HOMEDIR及其子目录(只)供应文件。仅使用URI,  *忽略所有页眉和HTTP参数。  * / 私人响应serveFile(字符串URI,地图&LT;字符串,字符串&GT;头,         档案文件中,字符串MIME){     应对资源;     尝试 {         //计算ETAG         字符串ETAG = Integer.toHexString((file.getAbsolutePath()                 + file.lastModified()++ file.length()),散列code())。         //支持(简单)跳绳:         长startFrom = 0;         长ENDAT = -1;         字符串范围= header.get(范围);         如果(范围!= NULL){             如果(range.startsWith(字节=)){                 范围= range.substring(字节=长度());                 INT减去= range.indexOf(' - ');                 尝试 {                     如果(负大于0){                         startFrom =的Long.parseLong(范围                                 .substring(0,负));                         ENDAT =的Long.parseLong(range.substring(减+ 1));                     }                 }赶上(NumberFormatException的忽略){                 }             }         }         //改变返回code和跳绳时是添加内容范围头         //要求         长fileLen = file.length();         如果(范围=空&放大器;!&安培; startFrom&GT; = 0){             如果(startFrom&GT; = fileLen){                 RES = createResponse(Response.Status.RANGE_NOT_SATISFIABLE,                         NanoHTTPD.MIME_PLAINTEXT,);                 res.addHeader(内容范围,字节0-0 /+ fileLen);                 res.addHeader(ETag的,ETAG);             } 其他 {                 如果(ENDAT℃,){                     ENDAT = fileLen - 1;                 }                 长newLen = ENDAT - startFrom + 1;                 如果(newLen℃,){                     newLen = 0;                 }                 最终长DATALEN = newLen;                 的FileInputStream FIS =新的FileInputStream(文件){                     @覆盖                     可公​​众诠释()抛出IOException异常{                         返回(INT)DATALEN;                     }                 };                 fis.skip(startFrom);                 RES = createResponse(Response.Status.PARTIAL_CONTENT,哑剧,                         FIS);                 res.addHeader(内容长度,+ DATALEN);                 res.addHeader(内容范围,字节+ startFrom + -                         + ENDAT +/+ fileLen);                 res.addHeader(ETag的,ETAG);             }         } 其他 {             如果(etag.equals(header.get(如果 - 无 - 匹配)))                 RES = createResponse(Response.Status.NOT_MODIFIED,默剧,);             其他 {                 RES = createResponse(Response.Status.OK,哑剧,                         新的FileInputStream(文件));                 res.addHeader(内容长度,+ fileLen);                 res.addHeader(ETag的,ETAG);             }         }     }赶上(IOException异常IOE){         RES = createResponse(Response.Status.FORBIDDEN,                 NanoHTTPD.MIME_PLAINTEXT,禁止访问:读取文件失败。);     }     返回水库; }

I've written a small Android server using NanoHTTPD. It can serve an HTML file well (web page located at sdcard/www/index.html). Can anybody please help me find out how can I serve an audio or video file instead of an html page using NanoHTTPD? Forgive me if the question seems silly, as I'm new to HTTP! Here is my server side code (I've replaced the webpage path to that of an audio file):

package com.example.zserver;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;

public class MainActivity extends Activity {

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

        server = new WebServer();
        try {
            server.start();
        } catch(IOException ioe) {
            Log.w("Httpd", "The server could not start.");
        }
        Log.w("Httpd", "Web server initialized.");

    }
    @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 {
                // Opening file from SD Card
                File root = Environment.getExternalStorageDirectory();
                FileReader index = new FileReader(root.getAbsolutePath() +
                        "/www/music.mp3");              
                BufferedReader reader = new BufferedReader(index);
                String line = "";
                while ((line = reader.readLine()) != null) {
                    answer += line;
                }

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


            return new NanoHTTPD.Response(answer);
        }
    }

}

I've included necessary use-permissions in my code:

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

Any help would be appreciated. Thanks in advance!

EDIT, Added permission:

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

EDIT1: Canonical way of reading and writing to buffers:

int read;
        int n=1;
        while((read = dis.read(mybytearray)) != -1){
            dos.write(mybytearray, 0, read);}

解决方案

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

这篇关于如何使用NanoHTTPD担任SD卡的文件(里面安卓)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆