2013-10-14 88 views
14

我使用NanoHTTPD寫了一個小的Android服務器。它可以很好地服務一個HTML文件(位於sdcard/www/index.html的網頁)。任何人都可以幫助我找出如何使用NanoHTTPD提供音頻或視頻文件而不是HTML頁面?如果這個問題看起來很愚蠢,請原諒我,因爲我是HTTP的新手!這裏是我的服務器端代碼(我把它換成網頁路徑,音頻文件):如何使用服務上NanoHTTPD SD卡文件(裏面的Android)

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

} 

我已經包括在我的代碼需要使用的權限:

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

任何幫助將不勝感激。提前致謝!

編輯,添加權限:

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

EDIT1:讀數的規範方式和寫入緩衝區:

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

回答

35

首先,你需要確保你的服務的媒體文件時,正確設置MIME類型。

其次,使用FileReader來讀取MP3文件不會太遠,而應該提供NanoHTTPD和InputStream

下面是你的代碼,它提供一個MP3文件的工作修改後的版本。通過將mimetype設置爲audio/mpeg,您可以讓瀏覽器決定如何處理此內容。例如,在Chrome中,集成音樂播放器將啓動並播放該文件。

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

編輯:很多人一直在問如何使音頻文件播放,有損使用範圍請求,我會證明這一點下面

爲了使音頻文件可查找,範圍請求使用這使HTTP客戶端能夠以塊的形式檢索部分音頻文件。確保您使用PARTIAL_CONTENT響應狀態(HTTP 206)提供文件。示例實現在NanoHTTPD的示例代碼中找到:SimpleWebserver.java

我在執行,而不是直接在服務方法返回一個NanoHTTPD響應,我創建一個名爲「servefile」這是我作爲處理響應使用另一種方法範圍請求,如下所示。此代碼是上面鏈接的SimpleWebServer.java的修改實現。

@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; 
} 
+0

非常感謝!它適用於較小的媒體。我想知道如何包含規範的讀/寫方式(使用緩衝區),以便可以傳輸更大的文件。在自定義協議中,它很簡單。我只是從一個緩衝區讀取(由數據輸入流填充),同時將其寫入數據輸出流。我不確定在HTTP中如何實現。我不能在每個數據塊中不添加標題的情況下連續發送數據。或者我錯過了一些東西。你能提供一個示例代碼嗎?詳情請參閱我的編輯部分。再次感謝。 –

+0

狀態。OK不允許mp3在html5播放器中查找,請告訴我用戶需要什麼標題才能讓它在播放器中搜索 – yourkishore

+0

我使用此代碼將我的手機中的mp3流式傳輸到瀏覽器。我收到錯誤'403 Forbidden(/)'。如何解決這個問題? –

0

不要你還需要添加的權限android.permission.READ_EXTERNAL_STORAGE

因爲您正在閱讀文件。