2013-10-14 1 views
14

Я написал небольшой сервер Android с помощью NanoHTTPD. Он может хорошо служить HTML-файлу (веб-страница, расположенная по адресу sdcard/www/index.html). Может ли кто-нибудь помочь мне узнать, как я могу использовать аудио- или видеофайлы вместо html-страницы с помощью NanoHTTPD? Простите меня, если вопрос кажется глупым, поскольку я новичок в HTTP! Вот мой код на стороне сервера (я заменил путь к веб-страницы, что аудиофайла):Как подать файл на SD-карте с помощью NanoHTTPD (внутри 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"/> 

Любая помощь будет оценена. Заранее спасибо!

EDIT, Added разрешение:

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

EDIT1: Canonical способ чтения и записи в буфер:

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

ответ

35

Во-первых, вам нужно убедиться, что вы правильно задали тип изображения при обслуживании мультимедийных файлов.

Во-вторых, вы не получите очень далеко, прочитав файл MP3 по строчке, используя FileReader, вместо этого вы должны предоставить 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); 
    } 
} 

EDIT: много людей спрашивают, как сделать аудиофайл с помощью доступного для поиска запросов диапазона, я покажу это ниже

Для того, чтобы аудио файл доступного для поиска, используются запросы диапазона которые позволяют HTTP-клиентам извлекать части аудиофайла в кусках. Убедитесь, что вы обслуживаете файл со статусом ответа PARTIAL_CONTENT (HTTP 206). Пример реализации можно найти в примере кода NanoHTTPD: SimpleWebserver.java

В моей реализации вместо того, чтобы возвращать ответ NanoHTTPD непосредственно в методе serve, я создаю другой метод под названием «файл-файл», который я использую в качестве ответа для обработки запросы диапазона, как вы можете видеть ниже. Этот код является модифицированной реализацией 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

Потому что вы читаете файл.