release
FongMi 4 weeks ago
parent b5713c86ca
commit 718ea1d993
  1. 91
      app/src/main/java/com/fongmi/android/tv/server/process/Local.java

@ -18,9 +18,9 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.zip.CRC32;
import fi.iki.elonen.NanoHTTPD.IHTTPSession; import fi.iki.elonen.NanoHTTPD.IHTTPSession;
import fi.iki.elonen.NanoHTTPD.Response; import fi.iki.elonen.NanoHTTPD.Response;
@ -44,7 +44,7 @@ public class Local implements Process {
if (url.startsWith("/file")) return getFile(session.getHeaders(), url); if (url.startsWith("/file")) return getFile(session.getHeaders(), url);
if (url.startsWith("/upload")) return upload(session.getParms(), files); if (url.startsWith("/upload")) return upload(session.getParms(), files);
if (url.startsWith("/newFolder")) return newFolder(session.getParms()); if (url.startsWith("/newFolder")) return newFolder(session.getParms());
if (url.startsWith("/delFolder") || url.startsWith("/delFile")) return delFolder(session.getParms()); if (url.startsWith("/delFolder") || url.startsWith("/delFile")) return delete(session.getParms());
return null; return null;
} }
@ -77,46 +77,41 @@ public class Local implements Process {
return Nano.ok(); return Nano.ok();
} }
private Response delFolder(Map<String, String> params) { private Response delete(Map<String, String> params) {
String path = params.get("path"); String path = params.get("path");
Path.clear(Path.root(path)); Path.clear(Path.root(path));
return Nano.ok(); return Nano.ok();
} }
private Response getFolder(File root) { private Response getFolder(File dir) {
List<File> list = Path.list(root); File rootDir = Path.root();
JsonObject info = new JsonObject(); String rootPath = rootDir.getAbsolutePath();
info.addProperty("parent", root.equals(Path.root()) ? "." : root.getParent().replace(Path.rootPath(), ""));
if (list.isEmpty()) {
info.add("files", new JsonArray());
return Nano.ok(info.toString());
}
JsonArray files = new JsonArray(); JsonArray files = new JsonArray();
info.add("files", files); for (File file : Path.list(dir)) {
for (File file : list) {
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
obj.addProperty("name", file.getName()); obj.addProperty("name", file.getName());
obj.addProperty("path", file.getAbsolutePath().replace(Path.rootPath(), "")); obj.addProperty("path", relativeTo(file, rootPath));
obj.addProperty("time", format.format(new Date(file.lastModified()))); obj.addProperty("time", format.format(new Date(file.lastModified())));
obj.addProperty("dir", file.isDirectory() ? 1 : 0); obj.addProperty("dir", file.isDirectory() ? 1 : 0);
files.add(obj); files.add(obj);
} }
JsonObject info = new JsonObject();
info.addProperty("parent", parentOf(dir, rootDir, rootPath));
info.add("files", files);
return Nano.ok(info.toString()); return Nano.ok(info.toString());
} }
private Response getFile(Map<String, String> headers, File file, String mime) throws IOException { private Response getFile(Map<String, String> headers, File file, String mime) throws IOException {
long fileLen = file.length(); long fileLen = file.length();
String etag = etag(file, fileLen);
String ifNoneMatch = headers.get("if-none-match"); String ifNoneMatch = headers.get("if-none-match");
String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + fileLen).hashCode());
if (ifNoneMatch != null && (ifNoneMatch.equals("*") || ifNoneMatch.equals(etag))) { if (ifNoneMatch != null && (ifNoneMatch.equals("*") || ifNoneMatch.equals(etag))) {
return newFixedLengthResponse(Status.NOT_MODIFIED, mime, ""); return newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
} }
HttpRange range = HttpRange.from(fileLen, headers, etag); HttpRange range = HttpRange.from(fileLen, headers, etag);
if (!range.valid()) { if (!range.valid()) return createRangeNotSatisfiableResponse(fileLen);
return createRangeNotSatisfiableResponse(fileLen);
}
FileInputStream fis = new FileInputStream(file); FileInputStream fis = new FileInputStream(file);
robustSkip(fis, range.start); skip(fis, range.start);
Response res; Response res;
if (range.isPartial(fileLen)) { if (range.isPartial(fileLen)) {
res = newFixedLengthResponse(Status.PARTIAL_CONTENT, mime, fis, range.length); res = newFixedLengthResponse(Status.PARTIAL_CONTENT, mime, fis, range.length);
@ -130,57 +125,67 @@ public class Local implements Process {
return res; return res;
} }
private String etag(File file, long fileLen) {
CRC32 crc = new CRC32();
crc.update((file.getAbsolutePath() + file.lastModified() + fileLen).getBytes());
return Long.toHexString(crc.getValue());
}
private Response createRangeNotSatisfiableResponse(long fileLen) { private Response createRangeNotSatisfiableResponse(long fileLen) {
Response res = newFixedLengthResponse(Status.RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, ""); Response res = newFixedLengthResponse(Status.RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, "");
res.addHeader("Content-Range", "bytes */" + fileLen); res.addHeader("Content-Range", "bytes */" + fileLen);
return res; return res;
} }
private void robustSkip(InputStream fis, long bytesToSkip) throws IOException { private void skip(InputStream is, long bytesToSkip) throws IOException {
if (bytesToSkip <= 0) return; if (bytesToSkip <= 0) return;
long remaining = bytesToSkip; long remaining = bytesToSkip;
while (remaining > 0) { while (remaining > 0) {
long skipped = fis.skip(remaining); long skipped = is.skip(remaining);
if (skipped <= 0) { if (skipped <= 0) throw new IOException("Failed to skip desired number of bytes");
throw new IOException("Failed to skip desired number of bytes");
}
remaining -= skipped; remaining -= skipped;
} }
} }
private static String relativeTo(File file, String rootPath) {
String path = file.getAbsolutePath();
return path.startsWith(rootPath) ? path.substring(rootPath.length()) : path;
}
private static String parentOf(File dir, File rootDir, String rootPath) {
if (dir.equals(rootDir)) return ".";
File parent = dir.getParentFile();
if (parent == null || parent.equals(rootDir)) return "";
return relativeTo(parent, rootPath);
}
private record HttpRange(long start, long end, long length, boolean valid) { private record HttpRange(long start, long end, long length, boolean valid) {
public boolean isPartial(long fileTotalLength) { public boolean isPartial(long total) {
return this.length < fileTotalLength; return length < total;
}
public static HttpRange invalid() {
return new HttpRange(0, 0, 0, false);
} }
public static HttpRange from(long fileLen, Map<String, String> headers, String etag) { public static HttpRange from(long fileLen, Map<String, String> headers, String etag) {
long start = 0; long start = 0;
long end = fileLen - 1; long end = fileLen - 1;
String rangeHeader = headers.get("range"); String rangeHeader = headers.get("range");
String ifRangeHeader = headers.get("if-range"); String ifRange = headers.get("if-range");
if (ifRangeHeader != null && !ifRangeHeader.equals(etag)) { if (ifRange != null && !ifRange.equals(etag)) rangeHeader = null;
rangeHeader = null;
}
if (rangeHeader != null && rangeHeader.startsWith("bytes=")) { if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
try { try {
String[] parts = rangeHeader.substring(6).split("-", 2); String[] parts = rangeHeader.substring(6).split("-", 2);
if (!parts[0].isEmpty()) { if (!parts[0].isEmpty()) start = Long.parseLong(parts[0]);
start = Long.parseLong(parts[0]); if (parts.length > 1 && !parts[1].isEmpty()) end = Long.parseLong(parts[1]);
} if (start >= fileLen || start > end) return invalid();
if (parts.length > 1 && !parts[1].isEmpty()) {
end = Long.parseLong(parts[1]);
}
if (start >= fileLen || start > end) {
return new HttpRange(0, 0, 0, false);
}
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return new HttpRange(0, 0, 0, false); return invalid();
}
} }
if (end >= fileLen) {
end = fileLen - 1;
} }
if (end >= fileLen) end = fileLen - 1;
return new HttpRange(start, end, end - start + 1, true); return new HttpRange(start, end, end - start + 1, true);
} }
} }

Loading…
Cancel
Save