feat: support streaming response for search results (#5630)
Co-authored-by: manx98 <1323517022@qq.com>
This commit is contained in:
+61
-15
@@ -1,7 +1,12 @@
|
||||
import { fetchURL, removePrefix } from "./utils";
|
||||
import { fetchURL, removePrefix, StatusError } from "./utils";
|
||||
import url from "../utils/url";
|
||||
|
||||
export default async function search(base: string, query: string) {
|
||||
export default async function search(
|
||||
base: string,
|
||||
query: string,
|
||||
signal: AbortSignal,
|
||||
callback: (item: ResourceItem) => void
|
||||
) {
|
||||
base = removePrefix(base);
|
||||
query = encodeURIComponent(query);
|
||||
|
||||
@@ -9,19 +14,60 @@ export default async function search(base: string, query: string) {
|
||||
base += "/";
|
||||
}
|
||||
|
||||
const res = await fetchURL(`/api/search${base}?query=${query}`, {});
|
||||
const res = await fetchURL(`/api/search${base}?query=${query}`, { signal });
|
||||
if (!res.body) {
|
||||
throw new StatusError("000 No connection", 0);
|
||||
}
|
||||
try {
|
||||
// Try streaming approach first (modern browsers)
|
||||
if (res.body && typeof res.body.pipeThrough === "function") {
|
||||
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
|
||||
let buffer = "";
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (value) {
|
||||
buffer += value;
|
||||
}
|
||||
const lines = buffer.split(/\n/);
|
||||
let lastLine = lines.pop();
|
||||
// Save incomplete last line
|
||||
if (!lastLine) {
|
||||
lastLine = "";
|
||||
}
|
||||
buffer = lastLine;
|
||||
|
||||
let data = await res.json();
|
||||
|
||||
data = data.map((item: ResourceItem & { dir: boolean }) => {
|
||||
item.url = `/files${base}` + url.encodePath(item.path);
|
||||
|
||||
if (item.dir) {
|
||||
item.url += "/";
|
||||
for (const line of lines) {
|
||||
if (line) {
|
||||
const item = JSON.parse(line) as ResourceItem;
|
||||
item.url = `/files${base}` + url.encodePath(item.path);
|
||||
if (item.isDir) {
|
||||
item.url += "/";
|
||||
}
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
if (done) break;
|
||||
}
|
||||
} else {
|
||||
// Fallback for browsers without streaming support (e.g., Safari)
|
||||
const text = await res.text();
|
||||
const lines = text.split(/\n/);
|
||||
for (const line of lines) {
|
||||
if (line) {
|
||||
const item = JSON.parse(line) as ResourceItem;
|
||||
item.url = `/files${base}` + url.encodePath(item.path);
|
||||
if (item.isDir) {
|
||||
item.url += "/";
|
||||
}
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
// Check if the error is an intentional cancellation
|
||||
if (e instanceof Error && e.name === "AbortError") {
|
||||
throw new StatusError("000 No connection", 0, true);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user