如何使用NanoHttpd提供指定多文件服务并生成可访问URL
Got it, let's tackle this problem step by step. You already have a basic NanoHttpd setup serving a single file, and now you want to expand it to serve multiple specific files from the SD card without generating directory listings. Here's how to make it work:
We'll build a server that maintains a mapping between URL path fragments and your selected files. This way, only the files you specify are accessible via the network.
import fi.iki.elonen.NanoHTTPD; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class MultiFileServer extends NanoHTTPD { private final Map<String, File> fileMapping; // Constructor: Accept array of SD card file paths directly public MultiFileServer(int port, String[] sdCardFilePaths) throws IOException { super(port); fileMapping = new HashMap<>(); // Populate the mapping: Use filename (or relative path) as the URL endpoint for (String filePath : sdCardFilePaths) { File targetFile = new File(filePath); if (targetFile.exists() && targetFile.isFile()) { // Option 1: Use just the filename (e.g., "/vacation.jpg") String urlPath = "/" + targetFile.getName(); // Option 2: Use relative path from SD root to avoid duplicate filenames (e.g., "/Photos/vacation.jpg") // String sdRoot = Environment.getExternalStorageDirectory().getPath(); // String urlPath = "/" + filePath.replace(sdRoot, "").replace("//", "/"); fileMapping.put(urlPath, targetFile); } } } @Override public Response serve(IHTTPSession session) { String requestedUri = session.getUri(); // Check if the requested URI matches any of our served files if (fileMapping.containsKey(requestedUri)) { File fileToServe = fileMapping.get(requestedUri); try { // Auto-detect MIME type for the file String mimeType = NanoHTTPD.getMimeTypeForFile(fileToServe.getName()); return newChunkedResponse(Response.Status.OK, mimeType, new FileInputStream(fileToServe)); } catch (IOException e) { return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", "Failed to read file: " + e.getMessage()); } } // Return 404 if the requested file isn't in our whitelist return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "This file is not served by this server"); } }
Once you've picked multiple files from the SD card (via a file picker, for example), pass their paths directly to the server constructor:
// Example: Array of paths to files you selected from the SD card String[] selectedSdFiles = { "/sdcard/Documents/project-report.pdf", "/sdcard/Photos/summer-trip.jpg", "/sdcard/Music/background-track.mp3" }; try { // Start the server on port 8080 (choose any available port) MultiFileServer fileServer = new MultiFileServer(8080, selectedSdFiles); fileServer.start(); System.out.println("Multi-file server is running!"); } catch (IOException e) { e.printStackTrace(); }
To get shareable URLs, you'll need your device's local IP address plus the server port and the mapped URL path for each file. Here's a helper to generate these URLs:
import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; // Helper to get your device's local network IP (for access from other devices on the same network) private static String getLocalIp() { try { for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddr = enumIpAddr.nextElement(); if (!inetAddr.isLoopbackAddress() && inetAddr.isSiteLocalAddress()) { return inetAddr.getHostAddress(); } } } } catch (Exception e) { e.printStackTrace(); } return "127.0.0.1"; // Fallback to localhost if IP detection fails } // Generate and print URLs for all served files public void printAccessUrls(MultiFileServer server) { String serverIp = getLocalIp(); int port = server.getListeningPort(); System.out.println("\nAccessible URLs:"); for (Map.Entry<String, File> entry : server.fileMapping.entrySet()) { String fullUrl = "http://" + serverIp + ":" + port + entry.getKey(); System.out.println(entry.getValue().getName() + " → " + fullUrl); } }
- Duplicate Filenames: If you have files with the same name in different folders, use the relative path option (Option 2 in the server constructor) to avoid URL conflicts.
- Permissions: On Android, ensure you have the necessary storage permissions. For Android 10+, use scoped storage or request
MANAGE_EXTERNAL_STORAGEto access SD card files. - Security: This setup only serves the files you explicitly select, so you don't risk exposing your entire SD card to the network.
内容的提问来源于stack exchange,提问作者arjun

