J'ai écrit un petit serveur Android avec NanoHTTPD. Il peut bien servir un fichier HTML (page Web située à sdcard/www/index.html). Quelqu'un peut-il m'aider, s'il vous plaît, à m'aider à savoir comment puis-je diffuser un fichier audio ou vidéo au lieu d'une page HTML à l'aide de NanoHTTPD? Pardonnez-moi si la question semble idiote, car je suis nouveau sur HTTP! Voici le code côté serveur (j'ai remplacé le chemin de la page Web par celui d'un fichier audio):
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);
}
}
}
J'ai inclus les autorisations d'utilisation nécessaires dans mon code:
<uses-permission Android:name="Android.permission.INTERNET"/>
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>
Toute aide serait appréciée. Merci d'avance!
EDIT, autorisation ajoutée:
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE"/>
EDIT1: Manière canonique de lire et d’écrire dans les tampons:
int read;
int n=1;
while((read = dis.read(mybytearray)) != -1){
dos.write(mybytearray, 0, read);}
Tout d'abord, vous devez vous assurer que vous définissez correctement le type MIME lorsque vous distribuez des fichiers multimédias.
Deuxièmement, vous ne serez pas très loin en lisant un fichier MP3 ligne par ligne en utilisant FileReader
, mais vous devrez plutôt fournir à NanoHTTPD une InputStream
.
Vous trouverez ci-dessous une version modifiée de votre code, qui sert un fichier MP3. En définissant le type MIME sur audio/mpeg
, vous laissez le navigateur décider quoi faire avec ce contenu. Dans Chrome, par exemple, le lecteur de musique intégré est lancé et lit le fichier.
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: de nombreuses personnes ont demandé comment rendre le fichier audio accessible en utilisant des requêtes de plage. Je vais vous le montrer ci-dessous
Pour rendre le fichier audio accessible, des requêtes de plage sont utilisées, qui permettent aux clients HTTP de récupérer des parties du fichier audio par morceaux. Assurez-vous que vous fournissez le fichier avec le statut de réponse PARTIAL_CONTENT (HTTP 206). Vous trouverez un exemple d'implémentation dans l'exemple de code de NanoHTTPD: SimpleWebserver.Java
Dans mon implémentation, au lieu de renvoyer une réponse NanoHTTPD directement dans la méthode serve, je crée une autre méthode appelée "servefile" que j'utilise comme réponse pour gérer les demandes de plage, comme vous pouvez le voir ci-dessous. Ce code est une implémentation modifiée du fichier SimpleWebServer.Java I ci-dessus.
@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;
}
c'est le code pour servir n'importe quel type de fichier
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());
}
dans le fichier manifeste ajouter la permission this
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE"/>
Ne devez-vous pas également ajouter l'autorisation Android.permission.READ_EXTERNAL_STORAGE
Parce que tu lis un fichier.